qmakenodes.cpp 97.6 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con committed
2
**
3 4
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://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
12 13 14
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
15
**
16 17 18 19 20 21 22
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
con's avatar
con committed
23
**
hjk's avatar
hjk committed
24
****************************************************************************/
hjk's avatar
hjk committed
25

26 27 28 29 30
#include "qmakenodes.h"
#include "qmakeproject.h"
#include "qmakeprojectmanager.h"
#include "qmakeprojectmanagerconstants.h"
#include "qmakebuildconfiguration.h"
Tobias Hunger's avatar
Tobias Hunger committed
31
#include "qmakerunconfigurationfactory.h"
con's avatar
con committed
32 33

#include <projectexplorer/nodesvisitor.h>
34
#include <projectexplorer/projectexplorer.h>
Alessandro Portale's avatar
Alessandro Portale committed
35
#include <projectexplorer/projectexplorerconstants.h>
con's avatar
con committed
36
#include <coreplugin/editormanager/editormanager.h>
37
#include <coreplugin/editormanager/ieditor.h>
Jens Bache-Wiig's avatar
Jens Bache-Wiig committed
38
#include <coreplugin/fileiconprovider.h>
39
#include <coreplugin/documentmanager.h>
con's avatar
con committed
40 41 42
#include <coreplugin/icore.h>
#include <coreplugin/iversioncontrol.h>
#include <coreplugin/vcsmanager.h>
43
#include <coreplugin/dialogs/readonlyfilesdialog.h>
con's avatar
con committed
44

45 46
#include <extensionsystem/pluginmanager.h>

47
#include <projectexplorer/buildmanager.h>
Tobias Hunger's avatar
Tobias Hunger committed
48 49
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/target.h>
50
#include <projectexplorer/projecttree.h>
51
#include <qtsupport/profilereader.h>
Tobias Hunger's avatar
Tobias Hunger committed
52
#include <qtsupport/qtkitinformation.h>
53
#include <cpptools/generatedcodemodelsupport.h>
con's avatar
con committed
54

Daniel Teske's avatar
Daniel Teske committed
55 56
#include <resourceeditor/resourcenode.h>

57
#include <cpptools/cppmodelmanager.h>
58
#include <cpptools/cpptoolsconstants.h>
Friedemann Kleint's avatar
Friedemann Kleint committed
59

60
#include <utils/algorithm.h>
61
#include <utils/fileutils.h>
62
#include <utils/hostosinfo.h>
63
#include <utils/qtcprocess.h>
Eike Ziller's avatar
Eike Ziller committed
64
#include <utils/mimetypes/mimedatabase.h>
65
#include <utils/stringutils.h>
66
#include <utils/theme/theme.h>
67
#include <proparser/prowriter.h>
68
#include <proparser/qmakevfs.h>
hjk's avatar
hjk committed
69

70
#include <QApplication>
71 72 73 74 75
#include <QDebug>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QXmlStreamReader>
hjk's avatar
hjk committed
76

77
#include <QMessageBox>
78
#include <utils/QtConcurrentTools>
con's avatar
con committed
79

hjk's avatar
hjk committed
80
using namespace Core;
81
using namespace ProjectExplorer;
82
using namespace Utils;
hjk's avatar
hjk committed
83

84
// Static cached data in struct QmakeNodeStaticData providing information and icons
85 86 87 88
// for file types and the project. Do some magic via qAddPostRoutine()
// to make sure the icons do not outlive QApplication, triggering warnings on X11.

struct FileTypeDataStorage {
89
    FileType type;
90
    Theme::ImageFile themeImage;
91 92
    const char *typeName;
    const char *icon;
93
    const char *addFileFilter;
94 95 96
};

static const FileTypeDataStorage fileTypeDataStorage[] = {
97
    { HeaderType, Theme::ProjectExplorerHeader, QT_TRANSLATE_NOOP("QmakeProjectManager::QmakePriFileNode", "Headers"),
Alessandro Portale's avatar
Alessandro Portale committed
98
      ProjectExplorer::Constants::FILEOVERLAY_H, "*.h; *.hh; *.hpp; *.hxx;"},
99
    { SourceType, Theme::ProjectExplorerSource, QT_TRANSLATE_NOOP("QmakeProjectManager::QmakePriFileNode", "Sources"),
Alessandro Portale's avatar
Alessandro Portale committed
100
      ProjectExplorer::Constants::FILEOVERLAY_CPP, "*.c; *.cc; *.cpp; *.cp; *.cxx; *.c++;" },
101
    { FormType, Theme::ProjectExplorerForm, QT_TRANSLATE_NOOP("QmakeProjectManager::QmakePriFileNode", "Forms"),
Alessandro Portale's avatar
Alessandro Portale committed
102
      Constants::FILEOVERLAY_UI, "*.ui;" },
103
    { StateChartType, Theme::ProjectExplorerForm, QT_TRANSLATE_NOOP("QmakeProjectManager::QmakePriFileNode", "State charts"),
Alessandro Portale's avatar
Alessandro Portale committed
104
      ProjectExplorer::Constants::FILEOVERLAY_SCXML, "*.scxml;" },
105
    { ResourceType, Theme::ProjectExplorerResource, QT_TRANSLATE_NOOP("QmakeProjectManager::QmakePriFileNode", "Resources"),
Alessandro Portale's avatar
Alessandro Portale committed
106
      ProjectExplorer::Constants::FILEOVERLAY_QRC, "*.qrc;" },
107
    { QMLType, Theme::ProjectExplorerQML, QT_TRANSLATE_NOOP("QmakeProjectManager::QmakePriFileNode", "QML"),
Alessandro Portale's avatar
Alessandro Portale committed
108
      ProjectExplorer::Constants::FILEOVERLAY_QML, "*.qml;" },
109
    { UnknownFileType, Theme::ProjectExplorerOtherFiles, QT_TRANSLATE_NOOP("QmakeProjectManager::QmakePriFileNode", "Other files"),
Alessandro Portale's avatar
Alessandro Portale committed
110
      ProjectExplorer::Constants::FILEOVERLAY_UNKNOWN, "*;" }
111 112
};

113
class SortByPath
114
{
115
public:
116
    bool operator()(Node *a, Node *b)
117
    { return operator()(a->filePath(), b->filePath()); }
118
    bool operator()(Node *a, const FileName &b)
119
    { return operator()(a->filePath(), b); }
120
    bool operator()(const FileName &a, Node *b)
121
    { return operator()(a, b->filePath()); }
122
    // Compare as strings to correctly detect case-only file rename
123
    bool operator()(const FileName &a, const FileName &b)
124
    { return a.toString() < b.toString(); }
125
};
126

127
class QmakeNodeStaticData {
128 129 130
public:
    class FileTypeData {
    public:
131
        FileTypeData(FileType t = UnknownFileType,
132
                     const QString &tN = QString(),
133
                     const QString &aff = QString(),
134
                     const QIcon &i = QIcon()) :
135
        type(t), typeName(tN), addFileFilter(aff), icon(i) { }
con's avatar
con committed
136

137
        FileType type;
138
        QString typeName;
139
        QString addFileFilter;
140 141 142
        QIcon icon;
    };

143
    QmakeNodeStaticData();
144

145 146 147 148
    QVector<FileTypeData> fileTypeData;
    QIcon projectIcon;
};

149
static void clearQmakeNodeStaticData();
150

151
QmakeNodeStaticData::QmakeNodeStaticData()
152
{
153 154
    // File type data
    const unsigned count = sizeof(fileTypeDataStorage)/sizeof(FileTypeDataStorage);
155
    fileTypeData.reserve(count);
156 157 158 159

    // Overlay the SP_DirIcon with the custom icons
    const QSize desiredSize = QSize(16, 16);

160
    const QPixmap dirPixmap = qApp->style()->standardIcon(QStyle::SP_DirIcon).pixmap(desiredSize);
Friedemann Kleint's avatar
Friedemann Kleint committed
161
    for (unsigned i = 0 ; i < count; ++i) {
162
        QIcon overlayIcon;
Alessandro Portale's avatar
Alessandro Portale committed
163 164
        const QString iconFile = creatorTheme()->imageFile(fileTypeDataStorage[i].themeImage,
                                                           QString::fromLatin1(fileTypeDataStorage[i].icon));
165
        overlayIcon = QIcon(iconFile);
166
        QIcon folderIcon;
167
        folderIcon.addPixmap(FileIconProvider::overlayIcon(dirPixmap, overlayIcon));
168
        const QString desc = QCoreApplication::translate("QmakeProjectManager::QmakePriFileNode", fileTypeDataStorage[i].typeName);
169
        const QString filter = QString::fromUtf8(fileTypeDataStorage[i].addFileFilter);
170
        fileTypeData.push_back(QmakeNodeStaticData::FileTypeData(fileTypeDataStorage[i].type,
171
                                                                 desc, filter, folderIcon));
172 173
    }
    // Project icon
Alessandro Portale's avatar
Alessandro Portale committed
174
    const QString fileName = creatorTheme()->imageFile(Theme::ProjectFileIcon,
Alessandro Portale's avatar
Alessandro Portale committed
175
                                                       QLatin1String(ProjectExplorer::Constants::FILEOVERLAY_QT));
Alessandro Portale's avatar
Alessandro Portale committed
176
    const QIcon projectBaseIcon(fileName);
177
    const QPixmap projectPixmap = FileIconProvider::overlayIcon(dirPixmap, projectBaseIcon);
178
    projectIcon.addPixmap(projectPixmap);
179

180
    qAddPostRoutine(clearQmakeNodeStaticData);
181 182
}

183
Q_GLOBAL_STATIC(QmakeNodeStaticData, qmakeNodeStaticData)
184

185
static void clearQmakeNodeStaticData()
186
{
187 188
    qmakeNodeStaticData()->fileTypeData.clear();
    qmakeNodeStaticData()->projectIcon = QIcon();
con's avatar
con committed
189 190
}

191 192
enum { debug = 0 };

193 194
using namespace QmakeProjectManager;
using namespace QmakeProjectManager::Internal;
195

196 197 198 199 200 201
namespace QmakeProjectManager {
namespace Internal {
class EvalInput
{
public:
    QString projectDir;
202
    FileName projectFilePath;
203 204 205 206 207 208 209 210 211 212 213 214
    QString buildDirectory;
    QtSupport::ProFileReader *readerExact;
    QtSupport::ProFileReader *readerCumulative;
    ProFileGlobals *qmakeGlobals;
    QMakeVfs *qmakeVfs;
    bool isQt5;
};

class PriFileEvalResult
{
public:
    QStringList folders;
215 216
    QSet<FileName> recursiveEnumerateFiles;
    QMap<FileType, QSet<FileName> > foundFiles;
217 218
};

219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
class IncludedPriFile
{
public:
    Utils::FileName name;
    PriFileEvalResult result;
    QMap<Utils::FileName, IncludedPriFile *> children;
    QVector<ProFile *> proFilesExact;
    QVector<ProFile *> proFilesCumulative;

    ~IncludedPriFile()
    {
        qDeleteAll(children);
    }
};

234 235 236 237 238 239 240 241
class EvalResult
{
public:
    enum EvalResultState { EvalAbort, EvalFail, EvalPartial, EvalOk };
    EvalResultState state;
    QmakeProjectType projectType;

    QStringList subProjectsNotToDeploy;
242
    QSet<FileName> exactSubdirs;
243
    IncludedPriFile includedFiles;
244 245 246 247 248 249
    TargetInformation targetInformation;
    InstallsList installsList;
    QHash<QmakeVariable, QStringList> newVarValues;
    bool isDeployable;
    QStringList errors;
};
250 251 252 253

QString ProVirtualFolderNode::displayName() const
{
    return m_typeName;
254
}
255 256 257 258

QString ProVirtualFolderNode::addFileFilter() const
{
    return m_addFileFilter;
259
}
260

261 262 263
} // namespace Internal
} // namespace QMakeProjectManager

264
QmakePriFile::QmakePriFile(QmakeProjectManager::QmakePriFileNode *qmakePriFile)
265
    : IDocument(0), m_priFile(qmakePriFile)
266
{
267
    setId("Qmake.PriFile");
hjk's avatar
hjk committed
268
    setMimeType(QLatin1String(QmakeProjectManager::Constants::PROFILE_MIMETYPE));
269
    setFilePath(m_priFile->filePath());
270 271
}

272
Core::IDocument::ReloadBehavior QmakePriFile::reloadBehavior(ChangeTrigger state, ChangeType type) const
273
{
274 275 276
    Q_UNUSED(state)
    Q_UNUSED(type)
    return BehaviorSilent;
277 278
}

279
bool QmakePriFile::reload(QString *errorString, ReloadFlag flag, ChangeType type)
280
{
281
    Q_UNUSED(errorString)
282 283
    Q_UNUSED(flag)
    if (type == TypePermissions)
284
        return true;
285
    m_priFile->scheduleUpdate();
286
    return true;
287
}
288

con's avatar
con committed
289
/*!
290
  \class QmakePriFileNode
con's avatar
con committed
291 292 293
  Implements abstract ProjectNode class
  */

294
namespace QmakeProjectManager {
295

296 297
QmakePriFileNode::QmakePriFileNode(QmakeProject *project, QmakeProFileNode *qmakeProFileNode,
                                   const FileName &filePath)
con's avatar
con committed
298 299
        : ProjectNode(filePath),
          m_project(project),
300
          m_qmakeProFileNode(qmakeProFileNode),
301
          m_projectFilePath(filePath),
302
          m_projectDir(filePath.toFileInfo().absolutePath())
con's avatar
con committed
303
{
dt's avatar
dt committed
304
    Q_ASSERT(project);
305 306
    m_qmakePriFile = new QmakePriFile(this);
    Core::DocumentManager::addDocument(m_qmakePriFile);
307

308
    setDisplayName(filePath.toFileInfo().completeBaseName());
309
    setIcon(qmakeNodeStaticData()->projectIcon);
310 311
}

312
QmakePriFileNode::~QmakePriFileNode()
313 314
{
    watchFolders(QSet<QString>());
315
    delete m_qmakePriFile;
316 317
}

318
void QmakePriFileNode::scheduleUpdate()
319
{
320
    QtSupport::ProFileCacheManager::instance()->discardFile(m_projectFilePath.toString());
321
    m_qmakeProFileNode->scheduleUpdate(QmakeProFileNode::ParseLater);
con's avatar
con committed
322 323
}

Daniel Teske's avatar
Daniel Teske committed
324
namespace Internal {
325
struct InternalNode
326
{
327 328
    QList<InternalNode *> virtualfolders;
    QMap<QString, InternalNode *> subnodes;
329
    FileNameList files;
330 331
    FileType type = UnknownFileType;
    int priority = 0;
332
    QString displayName;
333
    QString typeName;
334
    QString addFileFilter;
335 336
    QString fullPath;
    QIcon icon;
337

338 339
    ~InternalNode()
    {
340
        qDeleteAll(virtualfolders);
341 342
        qDeleteAll(subnodes);
    }
343

344
    // Creates: a tree structure from a list of absolute file paths.
345 346 347 348 349 350 351 352 353 354
    // Empty directories are compressed into a single entry with a longer path.
    // * project
    //    * /absolute/path
    //       * file1
    //    * relative
    //       * path1
    //          * file1
    //          * file2
    //       * path2
    //          * file1
355
    // The function first creates a tree that looks like the directory structure, i.e.
356 357 358 359 360 361
    //    * /
    //       * absolute
    //          * path
    // ...
    // and afterwards calls compress() which merges directory nodes with single children, i.e. to
    //    * /absolute/path
362
    void create(const QString &projectDir, const QSet<FileName> &newFilePaths, FileType type)
363
    {
364
        static const QChar separator = QLatin1Char('/');
365 366 367
        const FileName projectDirFileName = FileName::fromString(projectDir);
        foreach (const FileName &file, newFilePaths) {
            FileName fileWithoutPrefix;
368
            bool isRelative;
369
            if (file.isChildOf(projectDirFileName)) {
370
                isRelative = true;
371
                fileWithoutPrefix = file.relativeChildPath(projectDirFileName);
372 373 374 375
            } else {
                isRelative = false;
                fileWithoutPrefix = file;
            }
376
            QStringList parts = fileWithoutPrefix.toString().split(separator, QString::SkipEmptyParts);
377
            if (!HostOsInfo::isWindowsHost() && !isRelative && parts.count() > 0)
378 379 380
                parts[0].prepend(separator);
            QStringListIterator it(parts);
            InternalNode *currentNode = this;
381
            QString path = (isRelative ? (projectDirFileName.toString() + QLatin1Char('/')) : QString());
382 383 384 385
            while (it.hasNext()) {
                const QString &key = it.next();
                if (it.hasNext()) { // key is directory
                    path += key;
386
                    if (!currentNode->subnodes.contains(path)) {
387 388 389
                        InternalNode *val = new InternalNode;
                        val->type = type;
                        val->fullPath = path;
390 391
                        val->displayName = key;
                        currentNode->subnodes.insert(path, val);
392 393
                        currentNode = val;
                    } else {
394
                        currentNode = currentNode->subnodes.value(path);
395
                    }
396 397
                    path += separator;
                } else { // key is filename
398
                    currentNode->files.append(file);
399 400 401
                }
            }
        }
402 403
        this->compress();
    }
404

405 406 407 408 409 410 411 412 413
    // Removes folder nodes with only a single sub folder in it
    void compress()
    {
        QMap<QString, InternalNode*> newSubnodes;
        QMapIterator<QString, InternalNode*> i(subnodes);
        while (i.hasNext()) {
            i.next();
            i.value()->compress();
            if (i.value()->files.isEmpty() && i.value()->subnodes.size() == 1) {
414
                // replace i.value() by i.value()->subnodes.begin()
415
                QString key = i.value()->subnodes.begin().key();
416
                InternalNode *keep = i.value()->subnodes.value(key);
417
                keep->displayName = i.value()->displayName + QDir::separator() + keep->displayName;
418
                newSubnodes.insert(key, keep);
419 420 421 422
                i.value()->subnodes.clear();
                delete i.value();
            } else {
                newSubnodes.insert(i.key(), i.value());
423 424
            }
        }
425 426
        subnodes = newSubnodes;
    }
427

428 429 430
    FolderNode *createFolderNode(InternalNode *node)
    {
        FolderNode *newNode = 0;
431 432 433
        if (node->typeName.isEmpty()) {
            newNode = new FolderNode(FileName::fromString(node->fullPath));
        } else {
434 435 436 437
            auto n = new ProVirtualFolderNode(FileName::fromString(node->fullPath),
                                                 node->priority, node->typeName);
            n->setAddFileFilter(node->addFileFilter);
            newNode = n;
438
        }
439 440 441 442 443 444 445

        newNode->setDisplayName(node->displayName);
        if (!node->icon.isNull())
            newNode->setIcon(node->icon);
        return newNode;
    }

446
    // Makes the projectNode's subtree below the given folder match this internal node's subtree
447
    void updateSubFolders(FolderNode *folder)
448
    {
449
        if (type == ResourceType)
Daniel Teske's avatar
Daniel Teske committed
450 451 452
            updateResourceFiles(folder);
        else
            updateFiles(folder, type);
453

454 455 456
        // updateFolders
        QMultiMap<QString, FolderNode *> existingFolderNodes;
        foreach (FolderNode *node, folder->subFolderNodes())
457
            if (node->nodeType() != ProjectNodeType && !dynamic_cast<ResourceEditor::ResourceTopLevelNode *>(node))
458
                existingFolderNodes.insert(node->filePath().toString(), node);
459

460 461 462 463 464
        QList<FolderNode *> foldersToRemove;
        QList<FolderNode *> foldersToAdd;
        typedef QPair<InternalNode *, FolderNode *> NodePair;
        QList<NodePair> nodesToUpdate;

465 466 467 468 469 470 471 472 473
        // Check virtual
        {
            QList<InternalNode *>::const_iterator it = virtualfolders.constBegin();
            QList<InternalNode *>::const_iterator end = virtualfolders.constEnd();
            for ( ; it != end; ++it) {
                bool found = false;
                QString path = (*it)->fullPath;
                QMultiMap<QString, FolderNode *>::const_iterator oldit
                        = existingFolderNodes.constFind(path);
474
                while (oldit != existingFolderNodes.constEnd() && oldit.key() == path) {
475 476
                    if (oldit.value()->nodeType() == VirtualFolderNodeType) {
                        VirtualFolderNode *vfn = dynamic_cast<VirtualFolderNode *>(oldit.value());
477 478 479 480 481 482 483 484 485 486 487 488 489 490
                        if (vfn->priority() == (*it)->priority) {
                            found = true;
                            break;
                        }
                    }
                    ++oldit;
                }
                if (found) {
                    nodesToUpdate << NodePair(*it, *oldit);
                } else {
                    FolderNode *newNode = createFolderNode(*it);
                    foldersToAdd << newNode;
                    nodesToUpdate << NodePair(*it, newNode);
                }
491
            }
492
        }
493 494 495 496 497 498 499 500 501 502
        // Check subnodes
        {
            QMap<QString, InternalNode *>::const_iterator it = subnodes.constBegin();
            QMap<QString, InternalNode *>::const_iterator end = subnodes.constEnd();

            for ( ; it != end; ++it) {
                bool found = false;
                QString path = it.value()->fullPath;
                QMultiMap<QString, FolderNode *>::const_iterator oldit
                        = existingFolderNodes.constFind(path);
503
                while (oldit != existingFolderNodes.constEnd() && oldit.key() == path) {
504
                    if (oldit.value()->nodeType() == FolderNodeType) {
505 506 507 508 509 510 511 512 513 514 515 516 517
                        found = true;
                        break;
                    }
                    ++oldit;
                }
                if (found) {
                    nodesToUpdate << NodePair(it.value(), *oldit);
                } else {
                    FolderNode *newNode = createFolderNode(it.value());
                    foldersToAdd << newNode;
                    nodesToUpdate << NodePair(it.value(), newNode);
                }
            }
518
        }
519

520 521 522 523 524 525 526 527 528 529
        QSet<FolderNode *> toKeep;
        foreach (const NodePair &np, nodesToUpdate)
            toKeep << np.second;

        QMultiMap<QString, FolderNode *>::const_iterator jit = existingFolderNodes.constBegin();
        QMultiMap<QString, FolderNode *>::const_iterator jend = existingFolderNodes.constEnd();
        for ( ; jit != jend; ++jit)
            if (!toKeep.contains(jit.value()))
                foldersToRemove << jit.value();

530
        if (!foldersToRemove.isEmpty())
531
            folder->removeFolderNodes(foldersToRemove);
532
        if (!foldersToAdd.isEmpty())
533
            folder->addFolderNodes(foldersToAdd);
534

535
        foreach (const NodePair &np, nodesToUpdate)
536
            np.first->updateSubFolders(np.second);
537 538 539
    }

    // Makes the folder's files match this internal node's file list
540
    void updateFiles(FolderNode *folder, FileType type)
541 542 543 544 545
    {
        QList<FileNode*> existingFileNodes;
        foreach (FileNode *fileNode, folder->fileNodes()) {
            if (fileNode->fileType() == type && !fileNode->isGenerated())
                existingFileNodes << fileNode;
546 547
        }

548
        QList<FileNode*> filesToRemove;
549
        FileNameList filesToAdd;
550 551

        SortByPath sortByPath;
552 553
        Utils::sort(files, sortByPath);
        Utils::sort(existingFileNodes, sortByPath);
554 555 556 557

        ProjectExplorer::compareSortedLists(existingFileNodes, files, filesToRemove, filesToAdd, sortByPath);

        QList<FileNode *> nodesToAdd;
558
        foreach (const FileName &file, filesToAdd)
559
            nodesToAdd << new FileNode(file, type, false);
560

561 562
        folder->removeFileNodes(filesToRemove);
        folder->addFileNodes(nodesToAdd);
563
    }
Daniel Teske's avatar
Daniel Teske committed
564 565 566 567

    // Makes the folder's files match this internal node's file list
    void updateResourceFiles(FolderNode *folder)
    {
568
        QList<FolderNode *> existingResourceNodes; // for resource special handling
Daniel Teske's avatar
Daniel Teske committed
569
        foreach (FolderNode *folderNode, folder->subFolderNodes()) {
570
            if (ResourceEditor::ResourceTopLevelNode *rn = dynamic_cast<ResourceEditor::ResourceTopLevelNode *>(folderNode))
Daniel Teske's avatar
Daniel Teske committed
571 572 573
                existingResourceNodes << rn;
        }

574
        QList<FolderNode *> resourcesToRemove;
575
        FileNameList resourcesToAdd;
Daniel Teske's avatar
Daniel Teske committed
576 577

        SortByPath sortByPath;
578 579
        Utils::sort(files, sortByPath);
        Utils::sort(existingResourceNodes, sortByPath);
Daniel Teske's avatar
Daniel Teske committed
580 581 582 583 584 585

        ProjectExplorer::compareSortedLists(existingResourceNodes, files, resourcesToRemove, resourcesToAdd, sortByPath);

        QList<FolderNode *> nodesToAdd;
        nodesToAdd.reserve(resourcesToAdd.size());

586
        foreach (const FileName &file, resourcesToAdd)
Daniel Teske's avatar
Daniel Teske committed
587 588 589 590 591 592
            nodesToAdd.append(new ResourceEditor::ResourceTopLevelNode(file, folder));

        folder->removeFolderNodes(resourcesToRemove);
        folder->addFolderNodes(nodesToAdd);

        foreach (FolderNode *fn, nodesToAdd)
593
            dynamic_cast<ResourceEditor::ResourceTopLevelNode *>(fn)->update();
Daniel Teske's avatar
Daniel Teske committed
594
    }
595
};
Daniel Teske's avatar
Daniel Teske committed
596
}
dt's avatar
dt committed
597

598
QStringList QmakePriFileNode::baseVPaths(QtSupport::ProFileReader *reader, const QString &projectDir, const QString &buildDir)
con's avatar
con committed
599
{
dt's avatar
dt committed
600 601 602
    QStringList result;
    if (!reader)
        return result;
603
    result += reader->absolutePathValues(QLatin1String("VPATH"), projectDir);
dt's avatar
dt committed
604
    result << projectDir; // QMAKE_ABSOLUTE_SOURCE_PATH
605
    result << buildDir;
dt's avatar
dt committed
606 607 608
    result.removeDuplicates();
    return result;
}
con's avatar
con committed
609

610
QStringList QmakePriFileNode::fullVPaths(const QStringList &baseVPaths, QtSupport::ProFileReader *reader,
611
                                       const QString &qmakeVariable, const QString &projectDir)
dt's avatar
dt committed
612 613 614 615
{
    QStringList vPaths;
    if (!reader)
        return vPaths;
616
    vPaths = reader->absolutePathValues(QLatin1String("VPATH_") + qmakeVariable, projectDir);
dt's avatar
dt committed
617 618 619 620 621
    vPaths += baseVPaths;
    vPaths.removeDuplicates();
    return vPaths;
}

622
QSet<FileName> QmakePriFileNode::recursiveEnumerate(const QString &folder)
623
{
624
    QSet<FileName> result;
625 626 627 628 629 630 631
    QDir dir(folder);
    dir.setFilter(dir.filter() | QDir::NoDotAndDotDot);
    foreach (const QFileInfo &file, dir.entryInfoList()) {
        if (file.isDir() && !file.isSymLink())
            result += recursiveEnumerate(file.absoluteFilePath());
        else if (!Core::EditorManager::isAutoSaveFile(file.fileName()))
            result += FileName(file);
632 633 634
    }
    return result;
}
dt's avatar
dt committed
635

636 637 638
PriFileEvalResult QmakePriFileNode::extractValues(const EvalInput &input,
                                                  QVector<ProFile *> includeFilesExact,
                                                  QVector<ProFile *> includeFilesCumlative,
639
                                                  const QList<QList<VariableAndVPathInformation>> &variableAndVPathInformation)
dt's avatar
dt committed
640
{
641
    PriFileEvalResult result;
642

643 644 645 646 647 648
    // Figure out DEPLOYMENT and INSTALL folders.
    // Ignore stuff from cumulative parse, as we are recursively enumerating
    // all the files from those folders and add watchers for them. That's too
    // dangerous if we get the folders wrong and enumerate the whole project
    // tree multiple times.
    QStringList dynamicVariables = dynamicVarNames(input.readerExact, input.isQt5);
649
    foreach (ProFile *includeFileExact, includeFilesExact)
650
        foreach (const QString &dynamicVar, dynamicVariables)
651
            result.folders += input.readerExact->values(dynamicVar, includeFileExact);
652

653 654
    for (int i=0; i < result.folders.size(); ++i) {
        const QFileInfo fi(result.folders.at(i));
655
        if (fi.isRelative())
656
            result.folders[i] = QDir::cleanPath(input.projectDir + QLatin1Char('/') + result.folders.at(i));
657 658
    }

659
    result.folders.removeDuplicates();
dt's avatar
dt committed
660

661
    // Remove non existing items and non folders
662 663
    QStringList::iterator it = result.folders.begin();
    while (it != result.folders.end()) {
dt's avatar
dt committed
664
        QFileInfo fi(*it);
dt's avatar
dt committed
665 666 667 668 669
        if (fi.exists()) {
            if (fi.isDir()) {
                // keep directories
                ++it;
            } else {
670
                // move files directly to recursiveEnumerateFiles
671
                result.recursiveEnumerateFiles << FileName::fromString(*it);
672
                it = result.folders.erase(it);
dt's avatar
dt committed
673 674 675
            }
        } else {
            // do remove non exsting stuff
676
            it = result.folders.erase(it);
dt's avatar
dt committed
677
        }
dt's avatar
dt committed
678 679
    }

680 681
    foreach (const QString &folder, result.folders)
        result.recursiveEnumerateFiles += recursiveEnumerate(folder);
682

683
    const QVector<QmakeNodeStaticData::FileTypeData> &fileTypes = qmakeNodeStaticData()->fileTypeData;
con's avatar
con committed
684
    // update files
685
    QFileInfo tmpFi;
686
    for (int i = 0; i < fileTypes.size(); ++i) {
687
        FileType type = fileTypes.at(i).type;
688
        const QList<VariableAndVPathInformation> &qmakeVariables = variableAndVPathInformation.at(i);
689
        QSet<FileName> newFilePaths;
690
        foreach (const VariableAndVPathInformation &qmakeVariable, qmakeVariables) {
691
            foreach (ProFile *includeFileExact, includeFilesExact) {
692
                QStringList tmp = input.readerExact->absoluteFileValues(qmakeVariable.variable, input.projectDir, qmakeVariable.vPathsExact, includeFileExact);
693 694 695
                foreach (const QString &t, tmp) {
                    tmpFi.setFile(t);
                    if (tmpFi.isFile())
696
                        newFilePaths += FileName::fromString(t);
697
                }
698
            }
699
            foreach (ProFile *includeFileCumlative, includeFilesCumlative) {
700
                QStringList tmp = input.readerCumulative->absoluteFileValues(qmakeVariable.variable, input.projectDir, qmakeVariable.vPathsCumulative, includeFileCumlative);
701 702 703
                foreach (const QString &t, tmp) {
                    tmpFi.setFile(t);
                    if (tmpFi.isFile())
704
                        newFilePaths += FileName::fromString(t);
705
                }
706
            }
707
        }
con's avatar
con committed
708

709 710
        result.foundFiles[type] = newFilePaths;
        result.recursiveEnumerateFiles.subtract(newFilePaths);
dt's avatar
dt committed
711 712
    }

713

dt's avatar
dt committed
714 715
    for (int i = 0; i < fileTypes.size(); ++i) {
        FileType type = fileTypes.at(i).type;
716
        QSet<FileName> newFilePaths = filterFilesProVariables(type, result.foundFiles[type]);
717 718 719 720 721 722 723 724 725 726 727 728
        newFilePaths += filterFilesRecursiveEnumerata(type, result.recursiveEnumerateFiles);
        result.foundFiles[type] = newFilePaths;
    }


    return result;
}

void QmakePriFileNode::update(const Internal::PriFileEvalResult &result)
{
    // add project file node
    if (m_fileNodes.isEmpty())
729
        addFileNodes(QList<FileNode *>() << new FileNode(m_projectFilePath, ProjectFileType, false));
730

731 732 733 734 735 736 737
    m_recursiveEnumerateFiles = result.recursiveEnumerateFiles;
    watchFolders(result.folders.toSet());

    InternalNode contents;
    const QVector<QmakeNodeStaticData::FileTypeData> &fileTypes = qmakeNodeStaticData()->fileTypeData;
    for (int i = 0; i < fileTypes.size(); ++i) {
        FileType type = fileTypes.at(i).type;
738
        const QSet<FileName> &newFilePaths = result.foundFiles.value(type);
739 740
        // We only need to save this information if
        // we are watching folders
741
        if (!result.folders.isEmpty())
742 743 744 745
            m_files[type] = newFilePaths;
        else
            m_files[type].clear();