cmakeproject.cpp 36.7 KB
Newer Older
1
/**************************************************************************
con's avatar
con committed
2
3
4
**
** This file is part of Qt Creator
**
hjk's avatar
hjk committed
5
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
con's avatar
con committed
6
**
7
** Contact: Nokia Corporation (qt-info@nokia.com)
con's avatar
con committed
8
**
9
** Commercial Usage
10
**
11
12
13
14
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
15
**
16
** GNU Lesser General Public License Usage
17
**
18
19
20
21
22
23
** 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.
24
**
25
** If you are unsure which license is appropriate for your use, please
hjk's avatar
hjk committed
26
** contact the sales department at http://qt.nokia.com/contact.
con's avatar
con committed
27
**
28
**************************************************************************/
hjk's avatar
hjk committed
29

con's avatar
con committed
30
31
32
#include "cmakeproject.h"
#include "cmakeprojectconstants.h"
#include "cmakeprojectnodes.h"
hjk's avatar
hjk committed
33
#include "cmakerunconfiguration.h"
Tobias Hunger's avatar
Tobias Hunger committed
34
#include "cmaketarget.h"
35
#include "makestep.h"
36
#include "cmakeopenprojectwizard.h"
dt's avatar
dt committed
37
#include "cmakebuildconfiguration.h"
dt's avatar
dt committed
38
#include "cmakeuicodemodelsupport.h"
hjk's avatar
hjk committed
39

40
#include <projectexplorer/projectexplorerconstants.h>
dt's avatar
dt committed
41
#include <projectexplorer/projectexplorer.h>
42
#include <projectexplorer/buildenvironmentwidget.h>
Tobias Hunger's avatar
Tobias Hunger committed
43
#include <projectexplorer/buildsteplist.h>
dt's avatar
dt committed
44
#include <projectexplorer/buildmanager.h>
con's avatar
con committed
45
#include <cpptools/cppmodelmanagerinterface.h>
dt's avatar
dt committed
46
#include <extensionsystem/pluginmanager.h>
dt's avatar
dt committed
47
#include <designer/formwindoweditor.h>
hjk's avatar
hjk committed
48
#include <utils/qtcassert.h>
dt's avatar
dt committed
49
#include <coreplugin/icore.h>
dt's avatar
dt committed
50
#include <coreplugin/editormanager/editormanager.h>
hjk's avatar
hjk committed
51

dt's avatar
dt committed
52
#include <QtCore/QMap>
con's avatar
con committed
53
#include <QtCore/QDebug>
hjk's avatar
hjk committed
54
#include <QtCore/QDir>
dt's avatar
dt committed
55
#include <QtCore/QDateTime>
hjk's avatar
hjk committed
56
#include <QtCore/QProcess>
57
#include <QtGui/QFormLayout>
dt's avatar
dt committed
58
#include <QtGui/QMainWindow>
59
#include <QtGui/QInputDialog>
con's avatar
con committed
60
61
62

using namespace CMakeProjectManager;
using namespace CMakeProjectManager::Internal;
63
using namespace ProjectExplorer;
64
65
66
67
68
69
70
71
72
73

// QtCreator CMake Generator wishlist:
// Which make targets we need to build to get all executables
// What is the make we need to call
// What is the actual compiler executable
// DEFINES

// Open Questions
// Who sets up the environment for cl.exe ? INCLUDEPATH and so on

74
75
76
/*!
  \class CMakeProject
*/
con's avatar
con committed
77
CMakeProject::CMakeProject(CMakeManager *manager, const QString &fileName)
78
79
80
    : m_manager(manager),
      m_fileName(fileName),
      m_rootNode(new CMakeProjectNode(m_fileName)),
81
      m_insideFileChanged(false),
dt's avatar
dt committed
82
83
      m_targetFactory(new CMakeTargetFactory(this)),
      m_lastEditor(0)
con's avatar
con committed
84
85
{
    m_file = new CMakeFile(this, fileName);
Tobias Hunger's avatar
Tobias Hunger committed
86
87
88

    connect(this, SIGNAL(addedTarget(ProjectExplorer::Target*)),
            SLOT(targetAdded(ProjectExplorer::Target*)));
89
90
91
92
}

CMakeProject::~CMakeProject()
{
dt's avatar
dt committed
93
94
95
96
97
98
99
100
101
102
103
    // Remove CodeModel support
    CppTools::CppModelManagerInterface *modelManager
            = ExtensionSystem::PluginManager::instance()->getObject<CppTools::CppModelManagerInterface>();
    QMap<QString, CMakeUiCodeModelSupport *>::const_iterator it, end;
    it = m_uiCodeModelSupport.constBegin();
    end = m_uiCodeModelSupport.constEnd();
    for (; it!=end; ++it) {
        modelManager->removeEditorSupport(it.value());
        delete it.value();
    }

104
    m_codeModelFuture.cancel();
105
106
107
    delete m_rootNode;
}

Tobias Hunger's avatar
Tobias Hunger committed
108
void CMakeProject::fileChanged(const QString &fileName)
dt's avatar
dt committed
109
{
Tobias Hunger's avatar
Tobias Hunger committed
110
111
112
113
    Q_UNUSED(fileName)
    if (!activeTarget() ||
        !activeTarget()->activeBuildConfiguration())
        return;
dt's avatar
dt committed
114

Tobias Hunger's avatar
Tobias Hunger committed
115
116
117
118
119
    if (m_insideFileChanged)
        return;
    m_insideFileChanged = true;
    changeActiveBuildConfiguration(activeTarget()->activeBuildConfiguration());
    m_insideFileChanged = false;
120
121
}

Tobias Hunger's avatar
Tobias Hunger committed
122
void CMakeProject::changeActiveBuildConfiguration(ProjectExplorer::BuildConfiguration *bc)
123
{
124
    if (!bc || bc->target() != activeTarget())
Tobias Hunger's avatar
Tobias Hunger committed
125
126
127
128
129
        return;

    CMakeBuildConfiguration * cmakebc(qobject_cast<CMakeBuildConfiguration *>(bc));
    if (!cmakebc)
        return;
130

131
132
133
    // Pop up a dialog asking the user to rerun cmake
    QFileInfo sourceFileInfo(m_fileName);

Tobias Hunger's avatar
Tobias Hunger committed
134
    QString cbpFile = CMakeManager::findCbpFile(QDir(bc->buildDirectory()));
135
    QFileInfo cbpFileFi(cbpFile);
136
137
    CMakeOpenProjectWizard::Mode mode = CMakeOpenProjectWizard::Nothing;
    if (!cbpFileFi.exists()) {
138
        mode = CMakeOpenProjectWizard::NeedToCreate;
139
140
    } else {
        foreach(const QString &file, m_watchedFiles) {
141
            if (QFileInfo(file).lastModified() > cbpFileFi.lastModified()) {
142
143
144
145
146
                mode = CMakeOpenProjectWizard::NeedToUpdate;
                break;
            }
        }
    }
147

148
    if (mode != CMakeOpenProjectWizard::Nothing) {
149
150
        CMakeOpenProjectWizard copw(m_manager,
                                    sourceFileInfo.absolutePath(),
Tobias Hunger's avatar
Tobias Hunger committed
151
                                    cmakebc->buildDirectory(),
152
                                    mode,
Tobias Hunger's avatar
Tobias Hunger committed
153
                                    cmakebc->environment());
154
        copw.exec();
Tobias Hunger's avatar
Tobias Hunger committed
155
        cmakebc->setMsvcVersion(copw.msvcVersion());
156
157
158
159
160
    }
    // reparse
    parseCMakeLists();
}

Tobias Hunger's avatar
Tobias Hunger committed
161
void CMakeProject::targetAdded(ProjectExplorer::Target *t)
162
{
Tobias Hunger's avatar
Tobias Hunger committed
163
    if (!t)
164
        return;
Tobias Hunger's avatar
Tobias Hunger committed
165
166
167

    connect(t, SIGNAL(activeBuildConfigurationChanged(ProjectExplorer::BuildConfiguration*)),
            SLOT(changeActiveBuildConfiguration(ProjectExplorer::BuildConfiguration*)));
168
169
}

170
void CMakeProject::changeBuildDirectory(CMakeBuildConfiguration *bc, const QString &newBuildDirectory)
171
{
172
    bc->setBuildDirectory(newBuildDirectory);
173
174
175
    parseCMakeLists();
}

176
177
178
179
180
QString CMakeProject::defaultBuildDirectory() const
{
    return projectDirectory() + QLatin1String("/qtcreator-build");
}

dt's avatar
dt committed
181
bool CMakeProject::parseCMakeLists()
182
{
Tobias Hunger's avatar
Tobias Hunger committed
183
184
185
186
    if (!activeTarget() ||
        !activeTarget()->activeBuildConfiguration())
        return false;

187
    // Find cbp file
Tobias Hunger's avatar
Tobias Hunger committed
188
    CMakeBuildConfiguration *activeBC = activeTarget()->activeBuildConfiguration();
dt's avatar
dt committed
189
    QString cbpFile = CMakeManager::findCbpFile(activeBC->buildDirectory());
190
191

    // setFolderName
192
    m_rootNode->setDisplayName(QFileInfo(cbpFile).completeBaseName());
193
    CMakeCbpParser cbpparser;
194
195
    // Parsing
    //qDebug()<<"Parsing file "<<cbpFile;
Tobias Hunger's avatar
Tobias Hunger committed
196
197
198
199
200
201
202
    if (!cbpparser.parseCbpFile(cbpFile)) {
        // TODO report error
        qDebug()<<"Parsing failed";
        // activeBC->updateToolChain(QString::null);
        emit buildTargetsChanged();
        return false;
    }
203

Tobias Hunger's avatar
Tobias Hunger committed
204
205
206
    // ToolChain
    // activeBC->updateToolChain(cbpparser.compilerName());
    m_projectName = cbpparser.projectName();
207
    m_rootNode->setDisplayName(cbpparser.projectName());
208

Tobias Hunger's avatar
Tobias Hunger committed
209
210
211
212
213
214
215
216
217
    //qDebug()<<"Building Tree";
    QList<ProjectExplorer::FileNode *> fileList = cbpparser.fileList();
    QSet<QString> projectFiles;
    if (cbpparser.hasCMakeFiles()) {
        fileList.append(cbpparser.cmakeFileList());
        foreach(const ProjectExplorer::FileNode *node, cbpparser.cmakeFileList())
            projectFiles.insert(node->path());
    } else {
        // Manually add the CMakeLists.txt file
218
        QString cmakeListTxt = projectDirectory() + "/CMakeLists.txt";
219
220
        bool generated = false;
        fileList.append(new ProjectExplorer::FileNode(cmakeListTxt, ProjectExplorer::ProjectFileType, generated));
Tobias Hunger's avatar
Tobias Hunger committed
221
222
        projectFiles.insert(cmakeListTxt);
    }
223

Tobias Hunger's avatar
Tobias Hunger committed
224
225
226
227
228
229
230
    QSet<QString> added = projectFiles;
    added.subtract(m_watchedFiles);
    foreach(const QString &add, added)
        m_watcher->addFile(add);
    foreach(const QString &remove, m_watchedFiles.subtract(projectFiles))
        m_watcher->removeFile(remove);
    m_watchedFiles = projectFiles;
231

Tobias Hunger's avatar
Tobias Hunger committed
232
233
234
235
    m_files.clear();
    foreach (ProjectExplorer::FileNode *fn, fileList)
        m_files.append(fn->path());
    m_files.sort();
con's avatar
con committed
236

Tobias Hunger's avatar
Tobias Hunger committed
237
    buildTree(m_rootNode, fileList);
238

Tobias Hunger's avatar
Tobias Hunger committed
239
240
    //qDebug()<<"Adding Targets";
    m_buildTargets = cbpparser.buildTargets();
dt's avatar
dt committed
241
//        qDebug()<<"Printing targets";
242
//        foreach(CMakeBuildTarget ct, m_buildTargets) {
dt's avatar
dt committed
243
244
245
246
247
//            qDebug()<<ct.title<<" with executable:"<<ct.executable;
//            qDebug()<<"WD:"<<ct.workingDirectory;
//            qDebug()<<ct.makeCommand<<ct.makeCleanCommand;
//            qDebug()<<"";
//        }
dt's avatar
dt committed
248

dt's avatar
dt committed
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264

    // TOOD this code ain't very pretty ...
    m_uicCommand.clear();
    QFile cmakeCache(activeBC->buildDirectory() + "/CMakeCache.txt");
    cmakeCache.open(QIODevice::ReadOnly);
    while (!cmakeCache.atEnd()) {
        QString line = cmakeCache.readLine();
        if (line.startsWith("QT_UIC_EXECUTABLE")) {
            if (int pos = line.indexOf('=')) {
                m_uicCommand = line.mid(pos + 1).trimmed();
            }
            break;
        }
    }
    cmakeCache.close();

Tobias Hunger's avatar
Tobias Hunger committed
265
    //qDebug()<<"Updating CodeModel";
dt's avatar
dt committed
266
    createUiCodeModelSupport();
267

Tobias Hunger's avatar
Tobias Hunger committed
268
269
270
271
272
273
274
275
276
277
    QStringList allIncludePaths;
    QStringList allFrameworkPaths;
    QList<ProjectExplorer::HeaderPath> allHeaderPaths = activeBC->toolChain()->systemHeaderPaths();
    foreach (const ProjectExplorer::HeaderPath &headerPath, allHeaderPaths) {
        if (headerPath.kind() == ProjectExplorer::HeaderPath::FrameworkHeaderPath)
            allFrameworkPaths.append(headerPath.path());
        else
            allIncludePaths.append(headerPath.path());
    }
    // This explicitly adds -I. to the include paths
278
    allIncludePaths.append(projectDirectory());
Tobias Hunger's avatar
Tobias Hunger committed
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294

    allIncludePaths.append(cbpparser.includeFiles());
    CppTools::CppModelManagerInterface *modelmanager =
            ExtensionSystem::PluginManager::instance()->getObject<CppTools::CppModelManagerInterface>();
    if (modelmanager) {
        CppTools::CppModelManagerInterface::ProjectInfo pinfo = modelmanager->projectInfo(this);
        if (pinfo.includePaths != allIncludePaths
            || pinfo.sourceFiles != m_files
            || pinfo.defines != activeBC->toolChain()->predefinedMacros()
            || pinfo.frameworkPaths != allFrameworkPaths)  {
            pinfo.includePaths = allIncludePaths;
            // TODO we only want C++ files, not all other stuff that might be in the project
            pinfo.sourceFiles = m_files;
            pinfo.defines = activeBC->toolChain()->predefinedMacros(); // TODO this is to simplistic
            pinfo.frameworkPaths = allFrameworkPaths;
            modelmanager->updateProjectInfo(pinfo);
295
296
            m_codeModelFuture.cancel();
            m_codeModelFuture = modelmanager->updateSourceFiles(pinfo.sourceFiles);
con's avatar
con committed
297
        }
Tobias Hunger's avatar
Tobias Hunger committed
298
    }
dt's avatar
dt committed
299

300
    emit buildTargetsChanged();
301
    emit fileListChanged();
dt's avatar
dt committed
302
    return true;
con's avatar
con committed
303
304
}

Tobias Hunger's avatar
Tobias Hunger committed
305
306
307
308
309
QList<CMakeBuildTarget> CMakeProject::buildTargets() const
{
    return m_buildTargets;
}

310
QStringList CMakeProject::buildTargetTitles() const
con's avatar
con committed
311
{
312
    QStringList results;
313
    foreach (const CMakeBuildTarget &ct, m_buildTargets) {
314
315
        if (ct.executable.isEmpty())
            continue;
316
        if (ct.title.endsWith(QLatin1String("/fast")))
317
            continue;
318
        results << ct.title;
319
    }
320
    return results;
con's avatar
con committed
321
322
}

323
bool CMakeProject::hasBuildTarget(const QString &title) const
324
{
325
    foreach (const CMakeBuildTarget &ct, m_buildTargets) {
326
327
        if (ct.executable.isEmpty())
            continue;
328
        if (ct.title.endsWith(QLatin1String("/fast")))
329
330
331
332
333
334
335
            continue;
        if (ct.title == title)
            return true;
    }
    return false;
}

336
void CMakeProject::gatherFileNodes(ProjectExplorer::FolderNode *parent, QList<ProjectExplorer::FileNode *> &list)
con's avatar
con committed
337
{
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
    foreach(ProjectExplorer::FolderNode *folder, parent->subFolderNodes())
        gatherFileNodes(folder, list);
    foreach(ProjectExplorer::FileNode *file, parent->fileNodes())
        list.append(file);
}

void CMakeProject::buildTree(CMakeProjectNode *rootNode, QList<ProjectExplorer::FileNode *> newList)
{
    // Gather old list
    QList<ProjectExplorer::FileNode *> oldList;
    gatherFileNodes(rootNode, oldList);
    qSort(oldList.begin(), oldList.end(), ProjectExplorer::ProjectNode::sortNodesByPath);
    qSort(newList.begin(), newList.end(), ProjectExplorer::ProjectNode::sortNodesByPath);

    // generate added and deleted list
    QList<ProjectExplorer::FileNode *>::const_iterator oldIt  = oldList.constBegin();
    QList<ProjectExplorer::FileNode *>::const_iterator oldEnd = oldList.constEnd();
    QList<ProjectExplorer::FileNode *>::const_iterator newIt  = newList.constBegin();
    QList<ProjectExplorer::FileNode *>::const_iterator newEnd = newList.constEnd();

    QList<ProjectExplorer::FileNode *> added;
    QList<ProjectExplorer::FileNode *> deleted;


    while(oldIt != oldEnd && newIt != newEnd) {
        if ( (*oldIt)->path() == (*newIt)->path()) {
            delete *newIt;
            ++oldIt;
            ++newIt;
        } else if ((*oldIt)->path() < (*newIt)->path()) {
            deleted.append(*oldIt);
            ++oldIt;
        } else {
            added.append(*newIt);
            ++newIt;
        }
    }

    while (oldIt != oldEnd) {
        deleted.append(*oldIt);
        ++oldIt;
    }

    while (newIt != newEnd) {
        added.append(*newIt);
        ++newIt;
    }

    // add added nodes
    foreach (ProjectExplorer::FileNode *fn, added) {
//        qDebug()<<"added"<<fn->path();
con's avatar
con committed
389
390
391
392
393
        // Get relative path to rootNode
        QString parentDir = QFileInfo(fn->path()).absolutePath();
        ProjectExplorer::FolderNode *folder = findOrCreateFolder(rootNode, parentDir);
        rootNode->addFileNodes(QList<ProjectExplorer::FileNode *>()<< fn, folder);
    }
394

Tobias Hunger's avatar
Tobias Hunger committed
395
    // remove old file nodes and check whether folder nodes can be removed
396
397
398
399
400
401
402
403
404
405
406
407
408
    foreach (ProjectExplorer::FileNode *fn, deleted) {
        ProjectExplorer::FolderNode *parent = fn->parentFolderNode();
//        qDebug()<<"removed"<<fn->path();
        rootNode->removeFileNodes(QList<ProjectExplorer::FileNode *>() << fn, parent);
        // Check for empty parent
        while (parent->subFolderNodes().isEmpty() && parent->fileNodes().isEmpty()) {
            ProjectExplorer::FolderNode *grandparent = parent->parentFolderNode();
            rootNode->removeFolderNodes(QList<ProjectExplorer::FolderNode *>() << parent, grandparent);
            parent = grandparent;
            if (parent == rootNode)
                break;
        }
    }
con's avatar
con committed
409
410
411
412
413
}

ProjectExplorer::FolderNode *CMakeProject::findOrCreateFolder(CMakeProjectNode *rootNode, QString directory)
{
    QString relativePath = QDir(QFileInfo(rootNode->path()).path()).relativeFilePath(directory);
414
    QStringList parts = relativePath.split(QLatin1Char('/'), QString::SkipEmptyParts);
con's avatar
con committed
415
    ProjectExplorer::FolderNode *parent = rootNode;
416
    QString path = QFileInfo(rootNode->path()).path();
hjk's avatar
hjk committed
417
    foreach (const QString &part, parts) {
418
419
        path += QLatin1Char('/');
        path += part;
con's avatar
con committed
420
421
        // Find folder in subFolders
        bool found = false;
hjk's avatar
hjk committed
422
        foreach (ProjectExplorer::FolderNode *folder, parent->subFolderNodes()) {
423
            if (folder->path() == path) {
con's avatar
con committed
424
425
426
427
428
429
430
431
                // yeah found something :)
                parent = folder;
                found = true;
                break;
            }
        }
        if (!found) {
            // No FolderNode yet, so create it
432
            ProjectExplorer::FolderNode *tmp = new ProjectExplorer::FolderNode(path);
433
            tmp->setDisplayName(part);
con's avatar
con committed
434
435
436
437
438
439
440
            rootNode->addFolderNodes(QList<ProjectExplorer::FolderNode *>() << tmp, parent);
            parent = tmp;
        }
    }
    return parent;
}

441
QString CMakeProject::displayName() const
con's avatar
con committed
442
{
443
    return m_projectName;
con's avatar
con committed
444
445
}

Tobias Hunger's avatar
Tobias Hunger committed
446
447
QString CMakeProject::id() const
{
Tobias Hunger's avatar
Tobias Hunger committed
448
    return QLatin1String(Constants::CMAKEPROJECT_ID);
Tobias Hunger's avatar
Tobias Hunger committed
449
450
}

con's avatar
con committed
451
452
453
454
455
Core::IFile *CMakeProject::file() const
{
    return m_file;
}

Tobias Hunger's avatar
Tobias Hunger committed
456
457
458
459
460
CMakeTargetFactory *CMakeProject::targetFactory() const
{
    return m_targetFactory;
}

461
CMakeManager *CMakeProject::projectManager() const
con's avatar
con committed
462
463
464
465
{
    return m_manager;
}

Tobias Hunger's avatar
Tobias Hunger committed
466
467
468
469
470
CMakeTarget *CMakeProject::activeTarget() const
{
    return static_cast<CMakeTarget *>(Project::activeTarget());
}

con's avatar
con committed
471
472
473
474
475
QList<ProjectExplorer::Project *> CMakeProject::dependsOn()
{
    return QList<Project *>();
}

dt's avatar
dt committed
476
ProjectExplorer::BuildConfigWidget *CMakeProject::createConfigWidget()
con's avatar
con committed
477
{
478
    return new CMakeBuildSettingsWidget(this);
con's avatar
con committed
479
480
}

dt's avatar
dt committed
481
QList<ProjectExplorer::BuildConfigWidget*> CMakeProject::subConfigWidgets()
con's avatar
con committed
482
{
dt's avatar
dt committed
483
    QList<ProjectExplorer::BuildConfigWidget*> list;
484
    list << new BuildEnvironmentWidget;
485
    return list;
con's avatar
con committed
486
487
488
489
490
491
492
493
494
495
}

ProjectExplorer::ProjectNode *CMakeProject::rootProjectNode() const
{
    return m_rootNode;
}


QStringList CMakeProject::files(FilesMode fileMode) const
{
496
    Q_UNUSED(fileMode)
con's avatar
con committed
497
498
499
    return m_files;
}

500
bool CMakeProject::fromMap(const QVariantMap &map)
con's avatar
con committed
501
{
502
503
    if (!Project::fromMap(map))
        return false;
con's avatar
con committed
504

Tobias Hunger's avatar
Tobias Hunger committed
505
    bool hasUserFile = activeTarget();
dt's avatar
dt committed
506
    if (!hasUserFile) {
Tobias Hunger's avatar
Tobias Hunger committed
507
508
509
        CMakeTarget *t = targetFactory()->create(this, QLatin1String(DEFAULT_CMAKE_TARGET_ID));
        Q_ASSERT(t);
        Q_ASSERT(t->activeBuildConfiguration());
Tobias Hunger's avatar
Tobias Hunger committed
510

dt's avatar
dt committed
511
512
513
        // Ask the user for where he wants to build it
        // and the cmake command line

514
        CMakeOpenProjectWizard copw(m_manager, projectDirectory(), Utils::Environment::systemEnvironment());
515
516
        if (copw.exec() != QDialog::Accepted)
            return false;
dt's avatar
dt committed
517

Tobias Hunger's avatar
Tobias Hunger committed
518
519
        CMakeBuildConfiguration *bc =
                static_cast<CMakeBuildConfiguration *>(t->buildConfigurations().at(0));
520
        bc->setMsvcVersion(copw.msvcVersion());
521
        if (!copw.buildDirectory().isEmpty())
522
            bc->setBuildDirectory(copw.buildDirectory());
dt's avatar
dt committed
523

Tobias Hunger's avatar
Tobias Hunger committed
524
        addTarget(t);
525
526
527
    } else {
        // We have a user file, but we could still be missing the cbp file
        // or simply run createXml with the saved settings
dt's avatar
dt committed
528
        QFileInfo sourceFileInfo(m_fileName);
Tobias Hunger's avatar
Tobias Hunger committed
529
        CMakeBuildConfiguration *activeBC = activeTarget()->activeBuildConfiguration();
530
        QString cbpFile = CMakeManager::findCbpFile(QDir(activeBC->buildDirectory()));
531
        QFileInfo cbpFileFi(cbpFile);
532
533

        CMakeOpenProjectWizard::Mode mode = CMakeOpenProjectWizard::Nothing;
534
        if (!cbpFileFi.exists())
535
            mode = CMakeOpenProjectWizard::NeedToCreate;
536
        else if (cbpFileFi.lastModified() < sourceFileInfo.lastModified())
537
            mode = CMakeOpenProjectWizard::NeedToUpdate;
538

539
        if (mode != CMakeOpenProjectWizard::Nothing) {
540
541
            CMakeOpenProjectWizard copw(m_manager,
                                        sourceFileInfo.absolutePath(),
542
                                        activeBC->buildDirectory(),
543
                                        mode,
544
                                        activeBC->environment());
545
546
            if (copw.exec() != QDialog::Accepted)
                return false;
547
            activeBC->setMsvcVersion(copw.msvcVersion());
dt's avatar
dt committed
548
        }
dt's avatar
dt committed
549
    }
550
551
552

    m_watcher = new ProjectExplorer::FileWatcher(this);
    connect(m_watcher, SIGNAL(fileChanged(QString)), this, SLOT(fileChanged(QString)));
Tobias Hunger's avatar
Tobias Hunger committed
553
554

    if (!parseCMakeLists()) // Gets the directory from the active buildconfiguration
555
        return false;
556

Tobias Hunger's avatar
Tobias Hunger committed
557
    if (!hasUserFile && hasBuildTarget("all")) {
558
        MakeStep *makeStep = qobject_cast<MakeStep *>(
Tobias Hunger's avatar
Tobias Hunger committed
559
                    activeTarget()->activeBuildConfiguration()->stepList(ProjectExplorer::Constants::BUILDSTEPS_BUILD)->at(0));
Tobias Hunger's avatar
Tobias Hunger committed
560
        Q_ASSERT(makeStep);
561
        makeStep->setBuildTarget("all", true);
Tobias Hunger's avatar
Tobias Hunger committed
562
    }
563

Tobias Hunger's avatar
Tobias Hunger committed
564
565
566
567
568
569
    foreach (Target *t, targets()) {
        connect(t, SIGNAL(activeBuildConfigurationChanged(ProjectExplorer::BuildConfiguration*)),
                this, SLOT(changeActiveBuildConfiguration(ProjectExplorer::BuildConfiguration*)));
        connect(t, SIGNAL(environmentChanged()),
                this, SLOT(changeEnvironment()));
    }
dt's avatar
dt committed
570
571
572
573
574
575
576
577
578
579

    connect(Core::EditorManager::instance(), SIGNAL(editorAboutToClose(Core::IEditor*)),
            this, SLOT(editorAboutToClose(Core::IEditor*)));

    connect(Core::EditorManager::instance(), SIGNAL(currentEditorChanged(Core::IEditor*)),
            this, SLOT(editorChanged(Core::IEditor*)));

    connect(ProjectExplorer::ProjectExplorerPlugin::instance()->buildManager(), SIGNAL(buildStateChanged(ProjectExplorer::Project*)),
            this, SLOT(buildStateChanged(ProjectExplorer::Project*)));

dt's avatar
dt committed
580
    return true;
con's avatar
con committed
581
582
}

583
CMakeBuildTarget CMakeProject::buildTargetForTitle(const QString &title)
584
{
585
    foreach(const CMakeBuildTarget &ct, m_buildTargets)
586
587
        if (ct.title == title)
            return ct;
588
    return CMakeBuildTarget();
589
590
}

dt's avatar
dt committed
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
QString CMakeProject::uicCommand() const
{
    return m_uicCommand;
}

QString CMakeProject::uiHeaderFile(const QString &uiFile)
{
    QDir srcDirRoot = QDir(projectDirectory());
    QString relativePath = srcDirRoot.relativeFilePath(uiFile);
    QDir buildDir = QDir(activeTarget()->activeBuildConfiguration()->buildDirectory());
    QString uiHeaderFilePath = buildDir.absoluteFilePath(relativePath);

    QFileInfo fi(uiHeaderFilePath);
    uiHeaderFilePath = fi.absolutePath();
    uiHeaderFilePath += QLatin1String("/ui_");
    uiHeaderFilePath += fi.completeBaseName();
    uiHeaderFilePath += QLatin1String(".h");
    return QDir::cleanPath(uiHeaderFilePath);
}

void CMakeProject::createUiCodeModelSupport()
{
//    qDebug()<<"creatUiCodeModelSupport()";
    CppTools::CppModelManagerInterface *modelManager
            = ExtensionSystem::PluginManager::instance()->getObject<CppTools::CppModelManagerInterface>();

    // First move all to
    QMap<QString, CMakeUiCodeModelSupport *> oldCodeModelSupport;
    oldCodeModelSupport = m_uiCodeModelSupport;
    m_uiCodeModelSupport.clear();

    // Find all ui files
    foreach (const QString &uiFile, m_files) {
        if (uiFile.endsWith(".ui")) {
            // UI file, not convert to
            QString uiHeaderFilePath = uiHeaderFile(uiFile);
            QMap<QString, CMakeUiCodeModelSupport *>::iterator it
                    = oldCodeModelSupport.find(uiFile);
            if (it != oldCodeModelSupport.end()) {
                //                qDebug()<<"updated old codemodelsupport";
                CMakeUiCodeModelSupport *cms = it.value();
                cms->setFileName(uiHeaderFilePath);
                m_uiCodeModelSupport.insert(it.key(), cms);
                oldCodeModelSupport.erase(it);
            } else {
                //                qDebug()<<"adding new codemodelsupport";
                CMakeUiCodeModelSupport *cms = new CMakeUiCodeModelSupport(modelManager, this, uiFile, uiHeaderFilePath);
                m_uiCodeModelSupport.insert(uiFile, cms);
                modelManager->addEditorSupport(cms);
            }
        }
    }

    // Remove old
    QMap<QString, CMakeUiCodeModelSupport *>::const_iterator it, end;
    end = oldCodeModelSupport.constEnd();
    for (it = oldCodeModelSupport.constBegin(); it!=end; ++it) {
        modelManager->removeEditorSupport(it.value());
        delete it.value();
    }
}

void CMakeProject::updateCodeModelSupportFromEditor(const QString &uiFileName,
                                                    const QString &contents)
{
    const QMap<QString, CMakeUiCodeModelSupport *>::const_iterator it =
            m_uiCodeModelSupport.constFind(uiFileName);
    if (it != m_uiCodeModelSupport.constEnd())
        it.value()->updateFromEditor(contents);
}

void CMakeProject::editorChanged(Core::IEditor *editor)
{
    // Handle old editor
    Designer::FormWindowEditor *lastFormEditor = qobject_cast<Designer::FormWindowEditor *>(m_lastEditor);
    if (lastFormEditor) {
        disconnect(lastFormEditor, SIGNAL(changed()), this, SLOT(uiEditorContentsChanged()));

        if (m_dirtyUic) {
            const QString contents = lastFormEditor->contents();
            updateCodeModelSupportFromEditor(lastFormEditor->file()->fileName(), contents);
            m_dirtyUic = false;
        }
    }

    m_lastEditor = editor;

    // Handle new editor
    if (Designer::FormWindowEditor *fw = qobject_cast<Designer::FormWindowEditor *>(editor))
        connect(fw, SIGNAL(changed()), this, SLOT(uiEditorContentsChanged()));
}

void CMakeProject::editorAboutToClose(Core::IEditor *editor)
{
    if (m_lastEditor == editor) {
        // Oh no our editor is going to be closed
        // get the content first
        Designer::FormWindowEditor *lastEditor = qobject_cast<Designer::FormWindowEditor *>(m_lastEditor);
        if (lastEditor) {
            disconnect(lastEditor, SIGNAL(changed()), this, SLOT(uiEditorContentsChanged()));
            if (m_dirtyUic) {
                const QString contents = lastEditor->contents();
                updateCodeModelSupportFromEditor(lastEditor->file()->fileName(), contents);
                m_dirtyUic = false;
            }
        }
        m_lastEditor = 0;
    }
}

void CMakeProject::uiEditorContentsChanged()
{
    // cast sender, get filename
    if (m_dirtyUic)
        return;
    Designer::FormWindowEditor *fw = qobject_cast<Designer::FormWindowEditor *>(sender());
    if (!fw)
        return;
    m_dirtyUic = true;
}

void CMakeProject::buildStateChanged(ProjectExplorer::Project *project)
{
    if (project == this) {
        if (!ProjectExplorer::ProjectExplorerPlugin::instance()->buildManager()->isBuilding(this)) {
            QMap<QString, CMakeUiCodeModelSupport *>::const_iterator it, end;
            end = m_uiCodeModelSupport.constEnd();
            for (it = m_uiCodeModelSupport.constBegin(); it != end; ++it) {
                it.value()->updateFromBuild();
            }
        }
    }
}

725
726
// CMakeFile

con's avatar
con committed
727
728
729
730
731
732
733
734
735
736
CMakeFile::CMakeFile(CMakeProject *parent, QString fileName)
    : Core::IFile(parent), m_project(parent), m_fileName(fileName)
{

}

bool CMakeFile::save(const QString &fileName)
{
    // Once we have an texteditor open for this file, we probably do
    // need to implement this, don't we.
737
    Q_UNUSED(fileName)
con's avatar
con committed
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
    return false;
}

QString CMakeFile::fileName() const
{
    return m_fileName;
}

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

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

QString CMakeFile::mimeType() const
{
    return Constants::CMAKEMIMETYPE;
}


bool CMakeFile::isModified() const
{
    return false;
}

bool CMakeFile::isReadOnly() const
{
    return true;
}

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

dt's avatar
dt committed
777
778
779
780
781
782
783
void CMakeFile::rename(const QString &newName)
{
    Q_ASSERT(false);
    Q_UNUSED(newName);
    // Can't happen....
}

784
Core::IFile::ReloadBehavior CMakeFile::reloadBehavior(ChangeTrigger state, ChangeType type) const
con's avatar
con committed
785
{
786
787
788
789
790
791
792
793
794
    Q_UNUSED(state)
    Q_UNUSED(type)
    return BehaviorSilent;
}

void CMakeFile::reload(ReloadFlag flag, ChangeType type)
{
    Q_UNUSED(flag)
    Q_UNUSED(type)
con's avatar
con committed
795
796
}

797
CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeProject *project)
dt's avatar
dt committed
798
    : m_project(project), m_buildConfiguration(0)
con's avatar
con committed
799
{
800
    QFormLayout *fl = new QFormLayout(this);
801
    fl->setContentsMargins(20, -1, 0, -1);
802
    fl->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow);
803
    setLayout(fl);
804
805
806
807
808
809
810

    // TODO add action to Build menu?
    QPushButton *runCmakeButton = new QPushButton("Run cmake");
    connect(runCmakeButton, SIGNAL(clicked()),
            this, SLOT(runCMake()));
    fl->addRow(tr("Reconfigure project:"), runCmakeButton);

811
812
813
814
815
816
817
818
819
820
821
822
    m_pathLineEdit = new QLineEdit(this);
    m_pathLineEdit->setReadOnly(true);

    QHBoxLayout *hbox = new QHBoxLayout();
    hbox->addWidget(m_pathLineEdit);

    m_changeButton = new QPushButton(this);
    m_changeButton->setText(tr("&Change"));
    connect(m_changeButton, SIGNAL(clicked()), this, SLOT(openChangeBuildDirectoryDialog()));
    hbox->addWidget(m_changeButton);

    fl->addRow("Build directory:", hbox);
con's avatar
con committed
823
824
825
826
827
828
829
}

QString CMakeBuildSettingsWidget::displayName() const
{
    return "CMake";
}

dt's avatar
dt committed
830
void CMakeBuildSettingsWidget::init(BuildConfiguration *bc)
con's avatar
con committed
831
{
832
833
    m_buildConfiguration = static_cast<CMakeBuildConfiguration *>(bc);
    m_pathLineEdit->setText(m_buildConfiguration->buildDirectory());
834
    if (m_buildConfiguration->buildDirectory() == m_project->projectDirectory())
835
836
        m_changeButton->setEnabled(false);
    else
837
        m_changeButton->setEnabled(true);
con's avatar
con committed
838
}
839

840
void CMakeBuildSettingsWidget::openChangeBuildDirectoryDialog()
841
{
842
    CMakeOpenProjectWizard copw(m_project->projectManager(),
843
                                m_project->projectDirectory(),
844
845
                                m_buildConfiguration->buildDirectory(),
                                m_buildConfiguration->environment());
846
    if (copw.exec() == QDialog::Accepted) {
dt's avatar
dt committed
847
        m_project->changeBuildDirectory(m_buildConfiguration, copw.buildDirectory());
848
        m_pathLineEdit->setText(m_buildConfiguration->buildDirectory());
849
    }
850
851
}

852
853
854
855
856
857
858
859
860
861
862
863
864
void CMakeBuildSettingsWidget::runCMake()
{
    // TODO skip build directory
    CMakeOpenProjectWizard copw(m_project->projectManager(),
                                m_project->projectDirectory(),
                                m_buildConfiguration->buildDirectory(),
                                CMakeOpenProjectWizard::WantToUpdate,
                                m_buildConfiguration->environment());
    if (copw.exec() == QDialog::Accepted) {
        m_project->parseCMakeLists();
    }
}

865
866
867
868
/////
// CMakeCbpParser
////

869
870
871
872
873
874
bool CMakeCbpParser::parseCbpFile(const QString &fileName)
{
    QFile fi(fileName);
    if (fi.exists() && fi.open(QFile::ReadOnly)) {
        setDevice(&fi);

hjk's avatar
hjk committed
875
        while (!atEnd()) {
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
            readNext();
            if (name() == "CodeBlocks_project_file") {
                parseCodeBlocks_project_file();
            } else if (isStartElement()) {
                parseUnknownElement();
            }
        }
        fi.close();
        m_includeFiles.sort();
        m_includeFiles.removeDuplicates();
        return true;
    }
    return false;
}

void CMakeCbpParser::parseCodeBlocks_project_file()
{
hjk's avatar
hjk committed
893
    while (!atEnd()) {
894
895
896
897
898
899
900
901
902
903
904
905
906
        readNext();
        if (isEndElement()) {
            return;
        } else if (name() == "Project") {
            parseProject();
        } else if (isStartElement()) {
            parseUnknownElement();
        }
    }
}

void CMakeCbpParser::parseProject()
{
hjk's avatar
hjk committed
907
    while (!atEnd()) {
908
909
910
        readNext();
        if (isEndElement()) {
            return;
911
912
        } else if (name() == "Option") {
            parseOption();
913
914
915
916
917
918
919
920
921
922
923
924
        } else if (name() == "Unit") {
            parseUnit();
        } else if (name() == "Build") {
            parseBuild();
        } else if (isStartElement()) {
            parseUnknownElement();
        }
    }
}

void CMakeCbpParser::parseBuild()
{
hjk's avatar
hjk committed
925
    while (!atEnd()) {
926
927
928
929
        readNext();
        if (isEndElement()) {
            return;
        } else if (name() == "Target") {
930
            parseBuildTarget();
931
932
933
934
935
936
        } else if (isStartElement()) {
            parseUnknownElement();
        }
    }
}

937
void CMakeCbpParser::parseBuildTarget()
938
{
939
940
    m_buildTargetType = false;
    m_buildTarget.clear();
dt's avatar
dt committed
941
942

    if (attributes().hasAttribute("title"))
943
        m_buildTarget.title = attributes().value("title").toString();
dt's avatar
dt committed
944
    while (!atEnd()) {
945
946
        readNext();
        if (isEndElement()) {
947
948
            if (m_buildTargetType || m_buildTarget.title == "all" || m_buildTarget.title == "install") {
                m_buildTargets.append(m_buildTarget);
dt's avatar
dt committed
949
            }
950
951
952
            return;
        } else if (name() == "Compiler") {
            parseCompiler();
dt's avatar
dt committed
953
        } else if (name() == "Option") {
954
            parseBuildTargetOption();
dt's avatar
dt committed
955
956
957
958
959
960
        } else if (isStartElement()) {
            parseUnknownElement();
        }
    }
}

961
void CMakeCbpParser::parseBuildTargetOption()
dt's avatar
dt committed
962
{
963
    if (attributes().hasAttribute("output")) {
964
        m_buildTarget.executable = attributes().value("output").toString();
965
    } else if (attributes().hasAttribute("type") && (attributes().value("type") == "1" || attributes().value("type") == "0")) {
966
        m_buildTargetType = true;
967
968
969
970
    } else if (attributes().hasAttribute("type") && (attributes().value("type") == "3" || attributes().value("type") == "2")) {
        m_buildTargetType = true;
        m_buildTarget.library = true;
    } else if (attributes().hasAttribute("working_dir")) {
971
        m_buildTarget.workingDirectory = attributes().value("working_dir").toString();
972
    }
dt's avatar
dt committed
973
    while (!atEnd()) {
dt's avatar
dt committed
974
975
976
977
978
979
980
981
982
983
984
        readNext();
        if (isEndElement()) {
            return;
        } else if (name() == "MakeCommand") {
            parseMakeCommand();
        } else if (isStartElement()) {
            parseUnknownElement();
        }
    }
}

985
986
987
988
989
990
991
992
993
QString CMakeCbpParser::projectName() const
{
    return m_projectName;
}

void CMakeCbpParser::parseOption()
{
    if (attributes().hasAttribute("title"))
        m_projectName = attributes().value("title").toString();
994

995
996
997
    if (attributes().hasAttribute("compiler"))
        m_compiler = attributes().value("compiler").toString();

998
999
1000
1001
1002
1003
1004
1005
    while (!atEnd()) {
        readNext();
        if (isEndElement()) {
            return;
        } else if(isStartElement()) {
            parseUnknownElement();
        }
    }
1006
1007
}

dt's avatar
dt committed
1008
1009
void CMakeCbpParser::parseMakeCommand()
{
dt's avatar
dt committed
1010
    while (!atEnd()) {
dt's avatar
dt committed
1011
1012
1013
1014
        readNext();
        if (isEndElement()) {
            return;
        } else if (name() == "Build") {
1015
            parseBuildTargetBuild();
dt's avatar
dt committed
1016
        } else if (name() == "Clean") {
1017
            parseBuildTargetClean();
dt's avatar
dt committed
1018
1019
1020
1021
1022
1023
        } else if (isStartElement()) {
            parseUnknownElement();
        }
    }
}

1024
void CMakeCbpParser::parseBuildTargetBuild()
dt's avatar
dt committed
1025
1026
{
    if (attributes().hasAttribute("command"))
1027
        m_buildTarget.makeCommand = attributes().value("command").toString();
dt's avatar
dt committed
1028
    while (!atEnd()) {
dt's avatar
dt committed
1029
1030
1031
1032
1033
1034
1035
1036
1037
        readNext();
        if (isEndElement()) {
            return;
        } else if (isStartElement()) {
            parseUnknownElement();
        }
    }
}

1038
void CMakeCbpParser::parseBuildTargetClean()
dt's avatar
dt committed
1039
1040
{
    if (attributes().hasAttribute("command"))
1041
        m_buildTarget.makeCleanCommand = attributes().value("command").toString();
dt's avatar
dt committed
1042
    while (!atEnd()) {
dt's avatar
dt committed
1043
1044
1045
        readNext();
        if (isEndElement()) {
            return;
1046
1047
1048
1049
1050
1051
1052
1053
        } else if (isStartElement()) {
            parseUnknownElement();
        }
    }
}

void CMakeCbpParser::parseCompiler()
{
hjk's avatar
hjk committed
1054
    while (!atEnd()) {
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
        readNext();
        if (isEndElement()) {
            return;
        } else if (name() == "Add") {
            parseAdd();
        } else if (isStartElement()) {
            parseUnknownElement();
        }
    }
}

void CMakeCbpParser::parseAdd()
{
    m_includeFiles.append(attributes().value("directory").toString());
hjk's avatar
hjk committed
1069
    while (!atEnd()) {
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
        readNext();
        if (isEndElement()) {
            return;
        } else if (isStartElement()) {
            parseUnknownElement();
        }
    }
}

void CMakeCbpParser::parseUnit()
{
    //qDebug()<<stream.attributes().value("filename");
    QString fileName = attributes().value("filename").toString();
dt's avatar
dt committed
1083
    m_parsingCmakeUnit = false;
hjk's avatar
hjk committed
1084
    while (!atEnd()) {
1085
1086
        readNext();
        if (isEndElement()) {
1087
            if (!fileName.endsWith(QLatin1String(".rule")) && !m_processedUnits.contains(fileName)) {
dt's avatar
dt committed
1088
                // Now check whether we found a virtual element beneath
1089
                if (m_parsingCmakeUnit) {
dt's avatar
dt committed
1090
                    m_cmakeFileList.append( new ProjectExplorer::FileNode(fileName, ProjectExplorer::ProjectFileType, false));
1091
                } else {
1092
1093
1094
1095
1096
1097
1098
                    bool generated = false;
                    QString onlyFileName = QFileInfo(fileName).fileName();
                    if (   (onlyFileName.startsWith("moc_") && onlyFileName.endsWith(".cxx"))
                        || (onlyFileName.startsWith("ui_") && onlyFileName.endsWith(".h"))
                        || (onlyFileName.startsWith("qrc_") && onlyFileName.endsWith(".cxx")))
                        generated = true;

1099
                    if (fileName.endsWith(QLatin1String(".qrc")))
1100
                        m_fileList.append( new ProjectExplorer::FileNode(fileName, ProjectExplorer::ResourceType, generated));
1101
                    else