cmakeproject.cpp 34 KB
Newer Older
1
/**************************************************************************
con's avatar
con committed
2
3
4
**
** This file is part of Qt Creator
**
5
** Copyright (c) 2009 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"
34
#include "makestep.h"
35
#include "cmakeopenprojectwizard.h"
36
#include "cmakebuildenvironmentwidget.h"
hjk's avatar
hjk committed
37

38
#include <projectexplorer/projectexplorerconstants.h>
con's avatar
con committed
39
#include <cpptools/cppmodelmanagerinterface.h>
dt's avatar
dt committed
40
#include <extensionsystem/pluginmanager.h>
hjk's avatar
hjk committed
41
#include <utils/qtcassert.h>
dt's avatar
dt committed
42
#include <coreplugin/icore.h>
hjk's avatar
hjk committed
43

dt's avatar
dt committed
44
#include <QtCore/QMap>
con's avatar
con committed
45
#include <QtCore/QDebug>
hjk's avatar
hjk committed
46
#include <QtCore/QDir>
dt's avatar
dt committed
47
#include <QtCore/QDateTime>
hjk's avatar
hjk committed
48
#include <QtCore/QProcess>
49
#include <QtGui/QFormLayout>
dt's avatar
dt committed
50
#include <QtGui/QMainWindow>
con's avatar
con committed
51
52
53

using namespace CMakeProjectManager;
using namespace CMakeProjectManager::Internal;
54
55
using ProjectExplorer::Environment;
using ProjectExplorer::EnvironmentItem;
56
57
58
59
60
61
62
63
64
65
66

// 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


con's avatar
con committed
67
CMakeProject::CMakeProject(CMakeManager *manager, const QString &fileName)
68
69
70
    : m_manager(manager),
      m_fileName(fileName),
      m_rootNode(new CMakeProjectNode(m_fileName)),
71
72
      m_toolChain(0),
      m_insideFileChanged(false)
con's avatar
con committed
73
74
{
    m_file = new CMakeFile(this, fileName);
75
76
77
78
79
}

CMakeProject::~CMakeProject()
{
    delete m_rootNode;
80
    delete m_toolChain;
81
82
}

83
84
85
86
87
88
89
void CMakeProject::slotActiveBuildConfiguration()
{
    // Pop up a dialog asking the user to rerun cmake
    QFileInfo sourceFileInfo(m_fileName);

    QString cbpFile = CMakeManager::findCbpFile(QDir(buildDirectory(activeBuildConfiguration())));
    QFileInfo cbpFileFi(cbpFile);
90
91
    CMakeOpenProjectWizard::Mode mode = CMakeOpenProjectWizard::Nothing;
    if (!cbpFileFi.exists()) {
92
        mode = CMakeOpenProjectWizard::NeedToCreate;
93
94
95
96
97
98
99
100
    } else {
        foreach(const QString &file, m_watchedFiles) {
            if (QFileInfo(file).lastModified() > sourceFileInfo.lastModified()) {
                mode = CMakeOpenProjectWizard::NeedToUpdate;
                break;
            }
        }
    }
101

102
    if (mode != CMakeOpenProjectWizard::Nothing) {
103
104
105
106
107
        CMakeOpenProjectWizard copw(m_manager,
                                    sourceFileInfo.absolutePath(),
                                    buildDirectory(activeBuildConfiguration()),
                                    mode,
                                    environment(activeBuildConfiguration()));
108
        copw.exec();
dt's avatar
dt committed
109
        setValue(activeBuildConfiguration(), "msvcVersion", copw.msvcVersion());
110
111
112
113
114
    }
    // reparse
    parseCMakeLists();
}

115
116
void CMakeProject::fileChanged(const QString &fileName)
{
117
    Q_UNUSED(fileName)
118
119
120
    if (m_insideFileChanged== true)
        return;
    m_insideFileChanged = true;
121
    slotActiveBuildConfiguration();
122
123
124
125
126
127
128
129
    m_insideFileChanged = false;
}

void CMakeProject::updateToolChain(const QString &compiler)
{
    //qDebug()<<"CodeBlocks Compilername"<<compiler
    ProjectExplorer::ToolChain *newToolChain = 0;
    if (compiler == "gcc") {
130
131
132
#ifdef Q_OS_WIN
        newToolChain = ProjectExplorer::ToolChain::createMinGWToolChain("gcc", QString());
#else
133
        newToolChain = ProjectExplorer::ToolChain::createGccToolChain("gcc");
134
#endif
135
    } else if (compiler == "msvc8") {
dt's avatar
dt committed
136
        newToolChain = ProjectExplorer::ToolChain::createMSVCToolChain(value(activeBuildConfiguration(), "msvcVersion").toString(), false);
137
    } else {
138
        // TODO other toolchains
139
140
141
142
143
144
145
146
147
148
149
150
        qDebug()<<"Not implemented yet!!! Qt Creator doesn't know which toolchain to use for"<<compiler;
    }

    if (ProjectExplorer::ToolChain::equals(newToolChain, m_toolChain)) {
        delete newToolChain;
        newToolChain = 0;
    } else {
        delete m_toolChain;
        m_toolChain = newToolChain;
    }
}

dt's avatar
dt committed
151
152
153
154
155
156
157
ProjectExplorer::ToolChain *CMakeProject::toolChain(const QString &buildConfiguration) const
{
    if (buildConfiguration != activeBuildConfiguration())
        qWarning()<<"CMakeProject asked for toolchain of a not active buildconfiguration";
    return m_toolChain;
}

158
159
160
161
162
163
164
165
166
167
168
void CMakeProject::changeBuildDirectory(const QString &buildConfiguration, const QString &newBuildDirectory)
{
    setValue(buildConfiguration, "buildDirectory", newBuildDirectory);
    parseCMakeLists();
}

QString CMakeProject::sourceDirectory() const
{
    return QFileInfo(m_fileName).absolutePath();
}

dt's avatar
dt committed
169
bool CMakeProject::parseCMakeLists()
170
{
171
    // Find cbp file
172
    QString cbpFile = CMakeManager::findCbpFile(buildDirectory(activeBuildConfiguration()));
173
174

    // setFolderName
175
    m_rootNode->setFolderName(QFileInfo(cbpFile).completeBaseName());
176
    CMakeCbpParser cbpparser;
177
178
179
180
181
    // Parsing
    //qDebug()<<"Parsing file "<<cbpFile;
    if (cbpparser.parseCbpFile(cbpFile)) {        
        // ToolChain
        updateToolChain(cbpparser.compilerName());
182

183
        m_projectName = cbpparser.projectName();
dt's avatar
dt committed
184
        m_rootNode->setFolderName(cbpparser.projectName());
185

186
187
        //qDebug()<<"Building Tree";

dt's avatar
dt committed
188

189
        QList<ProjectExplorer::FileNode *> fileList = cbpparser.fileList();
dt's avatar
dt committed
190

191
        QSet<QString> projectFiles;
dt's avatar
dt committed
192
193
        if (cbpparser.hasCMakeFiles()) {
            fileList.append(cbpparser.cmakeFileList());
194
195
            foreach(ProjectExplorer::FileNode *node, cbpparser.cmakeFileList())
                projectFiles.insert(node->path());
dt's avatar
dt committed
196
197
        } else {
            // Manually add the CMakeLists.txt file
198
199
200
            QString cmakeListTxt = sourceDirectory() + "/CMakeLists.txt";
            fileList.append(new ProjectExplorer::FileNode(cmakeListTxt, ProjectExplorer::ProjectFileType, false));
            projectFiles.insert(cmakeListTxt);
dt's avatar
dt committed
201
        }
202

203
204
205
206
207
208
209
210
211

        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;

dt's avatar
dt committed
212
        m_files.clear();
213
        foreach (ProjectExplorer::FileNode *fn, fileList)
con's avatar
con committed
214
215
216
            m_files.append(fn->path());
        m_files.sort();

217
218
219
220
        buildTree(m_rootNode, fileList);


        //qDebug()<<"Adding Targets";
dt's avatar
dt committed
221
        m_targets = cbpparser.targets();
dt's avatar
dt committed
222
223
224
225
226
227
228
//        qDebug()<<"Printing targets";
//        foreach(CMakeTarget ct, m_targets) {
//            qDebug()<<ct.title<<" with executable:"<<ct.executable;
//            qDebug()<<"WD:"<<ct.workingDirectory;
//            qDebug()<<ct.makeCommand<<ct.makeCleanCommand;
//            qDebug()<<"";
//        }
dt's avatar
dt committed
229

230
        //qDebug()<<"Updating CodeModel";
231
232
233
234
235
236
237
238
239
240

        QStringList allIncludePaths;
        QStringList allFrameworkPaths;
        QList<ProjectExplorer::HeaderPath> allHeaderPaths = m_toolChain->systemHeaderPaths();
        foreach (ProjectExplorer::HeaderPath headerPath, allHeaderPaths) {
            if (headerPath.kind() == ProjectExplorer::HeaderPath::FrameworkHeaderPath)
                allFrameworkPaths.append(headerPath.path());
            else
                allIncludePaths.append(headerPath.path());
        }
241
        // This explicitly adds -I. to the include paths
242
        allIncludePaths.append(sourceDirectory());
243

244
        allIncludePaths.append(cbpparser.includeFiles());
con's avatar
con committed
245
246
        CppTools::CppModelManagerInterface *modelmanager = ExtensionSystem::PluginManager::instance()->getObject<CppTools::CppModelManagerInterface>();
        if (modelmanager) {
247
            CppTools::CppModelManagerInterface::ProjectInfo pinfo = modelmanager->projectInfo(this);
248
249
250
251
252
253
254
255
256
257
258
259
            if (pinfo.includePaths != allIncludePaths
                || pinfo.sourceFiles != m_files
                || pinfo.defines != m_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 = m_toolChain->predefinedMacros(); // TODO this is to simplistic
                pinfo.frameworkPaths = allFrameworkPaths;
                modelmanager->updateProjectInfo(pinfo);
                modelmanager->updateSourceFiles(pinfo.sourceFiles);
            }
con's avatar
con committed
260
        }
dt's avatar
dt committed
261
262

        // Create run configurations for m_targets
263
        //qDebug()<<"Create run configurations of m_targets";
264
        QMultiMap<QString, QSharedPointer<CMakeRunConfiguration> > existingRunConfigurations;
dt's avatar
dt committed
265
        foreach(QSharedPointer<ProjectExplorer::RunConfiguration> cmakeRunConfiguration, runConfigurations()) {
dt's avatar
dt committed
266
            if (QSharedPointer<CMakeRunConfiguration> rc = cmakeRunConfiguration.objectCast<CMakeRunConfiguration>()) {
dt's avatar
dt committed
267
268
269
270
                existingRunConfigurations.insert(rc->title(), rc);
            }
        }

271
        bool setActive = existingRunConfigurations.isEmpty();
dt's avatar
dt committed
272
273
274
275
276
        foreach(const CMakeTarget &ct, m_targets) {
            if (ct.executable.isEmpty())
                continue;
            if (ct.title.endsWith("/fast"))
                continue;
277
278
            QList<QSharedPointer<CMakeRunConfiguration> > list = existingRunConfigurations.values(ct.title);
            if (!list.isEmpty()) {
dt's avatar
dt committed
279
                // Already exists, so override the settings...
280
281
282
283
284
285
286
287
                foreach (QSharedPointer<CMakeRunConfiguration> rc, list) {
                    //qDebug()<<"Updating Run Configuration with title"<<ct.title;
                    //qDebug()<<"  Executable new:"<<ct.executable<< "old:"<<rc->executable();
                    //qDebug()<<"  WD new:"<<ct.workingDirectory<<"old:"<<rc->workingDirectory();
                    rc->setExecutable(ct.executable);
                    rc->setWorkingDirectory(ct.workingDirectory);
                }
                existingRunConfigurations.remove(ct.title);
dt's avatar
dt committed
288
289
            } else {
                // Does not exist yet
290
291
                //qDebug()<<"Adding new run configuration with title"<<ct.title;
                //qDebug()<<"  Executable:"<<ct.executable<<"WD:"<<ct.workingDirectory;
dt's avatar
dt committed
292
293
294
                QSharedPointer<ProjectExplorer::RunConfiguration> rc(new CMakeRunConfiguration(this, ct.executable, ct.workingDirectory, ct.title));
                addRunConfiguration(rc);
                // The first one gets the honour of beeing the active one
295
                if (setActive) {
dt's avatar
dt committed
296
                    setActiveRunConfiguration(rc);
297
                    setActive = false;
dt's avatar
dt committed
298
299
300
                }
            }
        }
301
        QMultiMap<QString, QSharedPointer<CMakeRunConfiguration> >::const_iterator it =
dt's avatar
dt committed
302
303
304
                existingRunConfigurations.constBegin();
        for( ; it != existingRunConfigurations.constEnd(); ++it) {
            QSharedPointer<CMakeRunConfiguration> rc = it.value();
305
306
            //qDebug()<<"Removing old RunConfiguration with title:"<<rc->title();
            //qDebug()<<"  Executable:"<<rc->executable()<<rc->workingDirectory();
dt's avatar
dt committed
307
308
            removeRunConfiguration(rc);
        }
309
        //qDebug()<<"\n";
con's avatar
con committed
310
311
    } else {
        // TODO report error
dt's avatar
dt committed
312
        qDebug()<<"Parsing failed";
313
314
        delete m_toolChain;
        m_toolChain = 0;
dt's avatar
dt committed
315
        return false;
con's avatar
con committed
316
    }
dt's avatar
dt committed
317
    return true;
con's avatar
con committed
318
319
}

320
321
QString CMakeProject::buildParser(const QString &buildConfiguration) const
{
322
    Q_UNUSED(buildConfiguration)
323
324
    // TODO this is actually slightly wrong, but do i care?
    // this should call toolchain(buildConfiguration)
325
326
327
328
329
330
331
332
333
334
335
336
337
    if (!m_toolChain)
        return QString::null;
    if (m_toolChain->type() == ProjectExplorer::ToolChain::GCC
        || m_toolChain->type() == ProjectExplorer::ToolChain::LinuxICC
        || m_toolChain->type() == ProjectExplorer::ToolChain::MinGW) {
        return ProjectExplorer::Constants::BUILD_PARSER_GCC;
    } else if (m_toolChain->type() == ProjectExplorer::ToolChain::MSVC
               || m_toolChain->type() == ProjectExplorer::ToolChain::WINCE) {
        return ProjectExplorer::Constants::BUILD_PARSER_MSVC;
    }
    return QString::null;
}

338
QStringList CMakeProject::targets() const
con's avatar
con committed
339
{
340
    QStringList results;
341
342
343
344
345
    foreach (const CMakeTarget &ct, m_targets) {
        if (ct.executable.isEmpty())
            continue;
        if (ct.title.endsWith("/fast"))
            continue;
346
        results << ct.title;
347
    }
348
    return results;
con's avatar
con committed
349
350
}

351
void CMakeProject::gatherFileNodes(ProjectExplorer::FolderNode *parent, QList<ProjectExplorer::FileNode *> &list)
con's avatar
con committed
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
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
    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
404
405
406
407
408
        // Get relative path to rootNode
        QString parentDir = QFileInfo(fn->path()).absolutePath();
        ProjectExplorer::FolderNode *folder = findOrCreateFolder(rootNode, parentDir);
        rootNode->addFileNodes(QList<ProjectExplorer::FileNode *>()<< fn, folder);
    }
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423

    // remove old file nodes and check wheter folder nodes can be removed
    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
424
425
426
427
428
}

ProjectExplorer::FolderNode *CMakeProject::findOrCreateFolder(CMakeProjectNode *rootNode, QString directory)
{
    QString relativePath = QDir(QFileInfo(rootNode->path()).path()).relativeFilePath(directory);
429
    QStringList parts = relativePath.split("/", QString::SkipEmptyParts);
con's avatar
con committed
430
    ProjectExplorer::FolderNode *parent = rootNode;
hjk's avatar
hjk committed
431
    foreach (const QString &part, parts) {
con's avatar
con committed
432
433
        // Find folder in subFolders
        bool found = false;
hjk's avatar
hjk committed
434
        foreach (ProjectExplorer::FolderNode *folder, parent->subFolderNodes()) {
con's avatar
con committed
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
            if (QFileInfo(folder->path()).fileName() == part) {
                // yeah found something :)
                parent = folder;
                found = true;
                break;
            }
        }
        if (!found) {
            // No FolderNode yet, so create it
            ProjectExplorer::FolderNode *tmp = new ProjectExplorer::FolderNode(part);
            rootNode->addFolderNodes(QList<ProjectExplorer::FolderNode *>() << tmp, parent);
            parent = tmp;
        }
    }
    return parent;
}

QString CMakeProject::name() const
{
454
    return m_projectName;
con's avatar
con committed
455
456
}

457
458


con's avatar
con committed
459
460
461
462
463
Core::IFile *CMakeProject::file() const
{
    return m_file;
}

464
CMakeManager *CMakeProject::projectManager() const
con's avatar
con committed
465
466
467
468
469
470
471
472
473
474
475
476
477
478
{
    return m_manager;
}

QList<ProjectExplorer::Project *> CMakeProject::dependsOn()
{
    return QList<Project *>();
}

bool CMakeProject::isApplication() const
{
    return true;
}

479
480
481
482
483
484
ProjectExplorer::Environment CMakeProject::baseEnvironment(const QString &buildConfiguration) const
{
    Environment env = useSystemEnvironment(buildConfiguration) ? Environment(QProcess::systemEnvironment()) : Environment();
    return env;
}

con's avatar
con committed
485
486
ProjectExplorer::Environment CMakeProject::environment(const QString &buildConfiguration) const
{
487
488
489
490
491
492
493
    Environment env = baseEnvironment(buildConfiguration);
    env.modify(userEnvironmentChanges(buildConfiguration));
    return env;
}

void CMakeProject::setUseSystemEnvironment(const QString &buildConfiguration, bool b)
{
494
495
    if (b == useSystemEnvironment(buildConfiguration))
        return;
496
    setValue(buildConfiguration, "clearSystemEnvironment", !b);
497
    emit environmentChanged(buildConfiguration);
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
}

bool CMakeProject::useSystemEnvironment(const QString &buildConfiguration) const
{
    bool b = !(value(buildConfiguration, "clearSystemEnvironment").isValid() && value(buildConfiguration, "clearSystemEnvironment").toBool());
    return b;
}

QList<ProjectExplorer::EnvironmentItem> CMakeProject::userEnvironmentChanges(const QString &buildConfig) const
{
    return EnvironmentItem::fromStringList(value(buildConfig, "userEnvironmentChanges").toStringList());
}

void CMakeProject::setUserEnvironmentChanges(const QString &buildConfig, const QList<ProjectExplorer::EnvironmentItem> &diff)
{
513
514
515
    QStringList list = EnvironmentItem::toStringList(diff);
    if (list == value(buildConfig, "userEnvironmentChanges"))
        return;
516
    setValue(buildConfig, "userEnvironmentChanges", list);
517
    emit environmentChanged(buildConfig);
con's avatar
con committed
518
519
520
521
}

QString CMakeProject::buildDirectory(const QString &buildConfiguration) const
{
522
523
    QString buildDirectory = value(buildConfiguration, "buildDirectory").toString();
    if (buildDirectory.isEmpty())
524
        buildDirectory = sourceDirectory() + "/qtcreator-build";
525
    return buildDirectory;
con's avatar
con committed
526
527
}

dt's avatar
dt committed
528
ProjectExplorer::BuildConfigWidget *CMakeProject::createConfigWidget()
con's avatar
con committed
529
{
530
    return new CMakeBuildSettingsWidget(this);
con's avatar
con committed
531
532
}

dt's avatar
dt committed
533
QList<ProjectExplorer::BuildConfigWidget*> CMakeProject::subConfigWidgets()
con's avatar
con committed
534
{
dt's avatar
dt committed
535
    QList<ProjectExplorer::BuildConfigWidget*> list;
536
537
    list <<  new CMakeBuildEnvironmentWidget(this);
    return list;
con's avatar
con committed
538
539
}

540
 bool CMakeProject::newBuildConfiguration(const QString &buildConfiguration)
con's avatar
con committed
541
 {
542
     // Default to all
543
544
     if (targets().contains("all"))
         makeStep()->setBuildTarget(buildConfiguration, "all", true);
545

546
    CMakeOpenProjectWizard copw(projectManager(), sourceDirectory(), buildDirectory(buildConfiguration), environment(buildConfiguration));
547
548
    if (copw.exec() == QDialog::Accepted) {
        setValue(buildConfiguration, "buildDirectory", copw.buildDirectory());
dt's avatar
dt committed
549
        setValue(buildConfiguration, "msvcVersion", copw.msvcVersion());
550
        parseCMakeLists();
551
        return true;
552
    }
553
    return false;
con's avatar
con committed
554
555
556
557
558
559
560
561
562
563
 }

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


QStringList CMakeProject::files(FilesMode fileMode) const
{
564
    Q_UNUSED(fileMode)
con's avatar
con committed
565
566
567
568
569
    return m_files;
}

void CMakeProject::saveSettingsImpl(ProjectExplorer::PersistentSettingsWriter &writer)
{
dt's avatar
dt committed
570
    Project::saveSettingsImpl(writer);
con's avatar
con committed
571
572
}

573
574
575
576
577
578
579
580
581
582
MakeStep *CMakeProject::makeStep() const
{
    foreach (ProjectExplorer::BuildStep *bs, buildSteps()) {
        MakeStep *ms = qobject_cast<MakeStep *>(bs);
        if (ms)
            return ms;
    }
    return 0;
}

583

dt's avatar
dt committed
584
bool CMakeProject::restoreSettingsImpl(ProjectExplorer::PersistentSettingsReader &reader)
con's avatar
con committed
585
{
dt's avatar
dt committed
586
    Project::restoreSettingsImpl(reader);
dt's avatar
dt committed
587
    bool hasUserFile = !buildConfigurations().isEmpty();
588
    MakeStep *makeStep = 0;
dt's avatar
dt committed
589
590
591
592
    if (!hasUserFile) {
        // Ask the user for where he wants to build it
        // and the cmake command line

593
        CMakeOpenProjectWizard copw(m_manager, sourceDirectory(), ProjectExplorer::Environment::systemEnvironment());
594
        copw.exec();
dt's avatar
dt committed
595

596
        qDebug()<<"ccd.buildDirectory()"<<copw.buildDirectory();
dt's avatar
dt committed
597
598

        // Now create a standard build configuration
599
        makeStep = new MakeStep(this);
600

601
        insertBuildStep(0, makeStep);
602

603
        addBuildConfiguration("all");
dt's avatar
dt committed
604
        setValue("all", "msvcVersion", copw.msvcVersion());
605
606
607
        if (!copw.buildDirectory().isEmpty())
            setValue("all", "buildDirectory", copw.buildDirectory());
        //TODO save arguments somewhere copw.arguments()
dt's avatar
dt committed
608
609
610
611

        MakeStep *cleanMakeStep = new MakeStep(this);
        insertCleanStep(0, cleanMakeStep);
        cleanMakeStep->setValue("clean", true);
612
        setActiveBuildConfiguration("all");
613
614
615
    } 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
616
617
618
        QFileInfo sourceFileInfo(m_fileName);
        QStringList needToCreate;
        QStringList needToUpdate;
619
620
        QString cbpFile = CMakeManager::findCbpFile(QDir(buildDirectory(activeBuildConfiguration())));
        QFileInfo cbpFileFi(cbpFile);
621
622

        CMakeOpenProjectWizard::Mode mode = CMakeOpenProjectWizard::Nothing;
623
        if (!cbpFileFi.exists())
624
            mode = CMakeOpenProjectWizard::NeedToCreate;
625
        else if (cbpFileFi.lastModified() < sourceFileInfo.lastModified())
626
            mode = CMakeOpenProjectWizard::NeedToUpdate;
627

628
        if (mode != CMakeOpenProjectWizard::Nothing) {
629
630
631
632
633
            CMakeOpenProjectWizard copw(m_manager,
                                        sourceFileInfo.absolutePath(),
                                        buildDirectory(activeBuildConfiguration()),
                                        mode,
                                        environment(activeBuildConfiguration()));
dt's avatar
dt committed
634
            copw.exec();
dt's avatar
dt committed
635
            setValue(activeBuildConfiguration(), "msvcVersion", copw.msvcVersion());
dt's avatar
dt committed
636
        }
dt's avatar
dt committed
637
    }
638

639
640
641
    if (!hasUserFile && targets().contains("all"))
        makeStep->setBuildTarget("all", "all", true);

642
643
    m_watcher = new ProjectExplorer::FileWatcher(this);
    connect(m_watcher, SIGNAL(fileChanged(QString)), this, SLOT(fileChanged(QString)));
644
645
646
    bool result = parseCMakeLists(); // Gets the directory from the active buildconfiguration
    if (!result)
        return false;
647
648
649

    connect(this, SIGNAL(activeBuildConfigurationChanged()),
            this, SLOT(slotActiveBuildConfiguration()));
dt's avatar
dt committed
650
    return true;
con's avatar
con committed
651
652
}

653
654
655
656
657
658
659
660
CMakeTarget CMakeProject::targetForTitle(const QString &title)
{
    foreach(const CMakeTarget &ct, m_targets)
        if (ct.title == title)
            return ct;
    return CMakeTarget();
}

661
662
663
664
665
666
667
668
669
ProjectExplorer::ToolChain::ToolChainType CMakeProject::toolChainType() const
{
    if (m_toolChain)
        return m_toolChain->type();
    return ProjectExplorer::ToolChain::UNKNOWN;
}

// CMakeFile

con's avatar
con committed
670
671
672
673
674
675
676
677
678
679
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.
680
    Q_UNUSED(fileName)
con's avatar
con committed
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
    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;
}

void CMakeFile::modified(ReloadBehavior *behavior)
{
722
    Q_UNUSED(behavior)
con's avatar
con committed
723
724
}

725
726
CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeProject *project)
    : m_project(project)
con's avatar
con committed
727
{
728
    QFormLayout *fl = new QFormLayout(this);
729
    fl->setContentsMargins(20, -1, 0, -1);
730
    fl->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow);
731
    setLayout(fl);
732
733
    m_pathLineEdit = new QLineEdit(this);
    m_pathLineEdit->setReadOnly(true);
734
735
736
737
    // TODO currently doesn't work
    // since creating the cbp file also creates makefiles
    // and then cmake builds in that directory instead of shadow building
    // We need our own generator for that to work
738
739
740
741
742
743
744
745
746
747

    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
748
749
750
751
752
753
754
755
756
}

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

void CMakeBuildSettingsWidget::init(const QString &buildConfiguration)
{
757
    m_buildConfiguration = buildConfiguration;
758
759
760
761
    m_pathLineEdit->setText(m_project->buildDirectory(buildConfiguration));
    if (m_project->buildDirectory(buildConfiguration) == m_project->sourceDirectory())
        m_changeButton->setEnabled(false);
    else
762
        m_changeButton->setEnabled(true);
con's avatar
con committed
763
}
764

765
void CMakeBuildSettingsWidget::openChangeBuildDirectoryDialog()
766
{
767
768
769
770
    CMakeOpenProjectWizard copw(m_project->projectManager(),
                                m_project->sourceDirectory(),
                                m_project->buildDirectory(m_buildConfiguration),
                                m_project->environment(m_buildConfiguration));
771
772
773
774
    if (copw.exec() == QDialog::Accepted) {
        m_project->changeBuildDirectory(m_buildConfiguration, copw.buildDirectory());
        m_pathLineEdit->setText(m_project->buildDirectory(m_buildConfiguration));
    }
775
776
777
778
779
780
}

/////
// CMakeCbpParser
////

781
782
783
784
785
786
bool CMakeCbpParser::parseCbpFile(const QString &fileName)
{
    QFile fi(fileName);
    if (fi.exists() && fi.open(QFile::ReadOnly)) {
        setDevice(&fi);

hjk's avatar
hjk committed
787
        while (!atEnd()) {
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
            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
805
    while (!atEnd()) {
806
807
808
809
810
811
812
813
814
815
816
817
818
        readNext();
        if (isEndElement()) {
            return;
        } else if (name() == "Project") {
            parseProject();
        } else if (isStartElement()) {
            parseUnknownElement();
        }
    }
}

void CMakeCbpParser::parseProject()
{
hjk's avatar
hjk committed
819
    while (!atEnd()) {
820
821
822
        readNext();
        if (isEndElement()) {
            return;
823
824
        } else if (name() == "Option") {
            parseOption();
825
826
827
828
829
830
831
832
833
834
835
836
        } else if (name() == "Unit") {
            parseUnit();
        } else if (name() == "Build") {
            parseBuild();
        } else if (isStartElement()) {
            parseUnknownElement();
        }
    }
}

void CMakeCbpParser::parseBuild()
{
hjk's avatar
hjk committed
837
    while (!atEnd()) {
838
839
840
841
842
843
844
845
846
847
848
849
850
        readNext();
        if (isEndElement()) {
            return;
        } else if (name() == "Target") {
            parseTarget();
        } else if (isStartElement()) {
            parseUnknownElement();
        }
    }
}

void CMakeCbpParser::parseTarget()
{
dt's avatar
dt committed
851
    m_targetType = false;
dt's avatar
dt committed
852
853
854
855
    m_target.clear();

    if (attributes().hasAttribute("title"))
        m_target.title = attributes().value("title").toString();
dt's avatar
dt committed
856
    while (!atEnd()) {
857
858
        readNext();
        if (isEndElement()) {
859
            if (m_targetType || m_target.title == "all" || m_target.title == "install") {
dt's avatar
dt committed
860
                m_targets.append(m_target);
dt's avatar
dt committed
861
            }
862
863
864
            return;
        } else if (name() == "Compiler") {
            parseCompiler();
dt's avatar
dt committed
865
866
867
868
869
870
871
872
873
874
875
        } else if (name() == "Option") {
            parseTargetOption();
        } else if (isStartElement()) {
            parseUnknownElement();
        }
    }
}

void CMakeCbpParser::parseTargetOption()
{
    if (attributes().hasAttribute("output"))
dt's avatar
dt committed
876
        m_target.executable = attributes().value("output").toString();
877
    else if (attributes().hasAttribute("type") && (attributes().value("type") == "1" || attributes().value("type") == "0"))
dt's avatar
dt committed
878
        m_targetType = true;
dt's avatar
dt committed
879
880
    else if (attributes().hasAttribute("working_dir"))
        m_target.workingDirectory = attributes().value("working_dir").toString();
dt's avatar
dt committed
881
    while (!atEnd()) {
dt's avatar
dt committed
882
883
884
885
886
887
888
889
890
891
892
        readNext();
        if (isEndElement()) {
            return;
        } else if (name() == "MakeCommand") {
            parseMakeCommand();
        } else if (isStartElement()) {
            parseUnknownElement();
        }
    }
}

893
894
895
896
897
898
899
900
901
QString CMakeCbpParser::projectName() const
{
    return m_projectName;
}

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

903
904
905
    if (attributes().hasAttribute("compiler"))
        m_compiler = attributes().value("compiler").toString();

906
907
908
909
910
911
912
913
    while (!atEnd()) {
        readNext();
        if (isEndElement()) {
            return;
        } else if(isStartElement()) {
            parseUnknownElement();
        }
    }
914
915
}

dt's avatar
dt committed
916
917
void CMakeCbpParser::parseMakeCommand()
{
dt's avatar
dt committed
918
    while (!atEnd()) {
dt's avatar
dt committed
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
        readNext();
        if (isEndElement()) {
            return;
        } else if (name() == "Build") {
            parseTargetBuild();
        } else if (name() == "Clean") {
            parseTargetClean();
        } else if (isStartElement()) {
            parseUnknownElement();
        }
    }
}

void CMakeCbpParser::parseTargetBuild()
{
    if (attributes().hasAttribute("command"))
        m_target.makeCommand = attributes().value("command").toString();
dt's avatar
dt committed
936
    while (!atEnd()) {
dt's avatar
dt committed
937
938
939
940
941
942
943
944
945
946
947
948
949
        readNext();
        if (isEndElement()) {
            return;
        } else if (isStartElement()) {
            parseUnknownElement();
        }
    }
}

void CMakeCbpParser::parseTargetClean()
{
    if (attributes().hasAttribute("command"))
        m_target.makeCleanCommand = attributes().value("command").toString();
dt's avatar
dt committed
950
    while (!atEnd()) {
dt's avatar
dt committed
951
952
953
        readNext();
        if (isEndElement()) {
            return;
954
955
956
957
958
959
960
961
        } else if (isStartElement()) {
            parseUnknownElement();
        }
    }
}

void CMakeCbpParser::parseCompiler()
{
hjk's avatar
hjk committed
962
    while (!atEnd()) {
963
964
965
966
967
968
969
970
971
972
973
974
975
976
        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
977
    while (!atEnd()) {
978
979
980
981
982
983
984
985
986
987
988
989
990
        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
991
    m_parsingCmakeUnit = false;
hjk's avatar
hjk committed
992
    while (!atEnd()) {
993
994
        readNext();
        if (isEndElement()) {
dt's avatar
dt committed
995
996
            if (!fileName.endsWith(".rule") && !m_processedUnits.contains(fileName)) {
                // Now check whether we found a virtual element beneath
997
                if (m_parsingCmakeUnit) {
dt's avatar
dt committed
998
                    m_cmakeFileList.append( new ProjectExplorer::FileNode(fileName, ProjectExplorer::ProjectFileType, false));
999
1000
1001
1002
1003
1004
                } else {
                    if (fileName.endsWith(".qrc"))
                        m_fileList.append( new ProjectExplorer::FileNode(fileName, ProjectExplorer::ResourceType, false));
                    else
                        m_fileList.append( new ProjectExplorer::FileNode(fileName, ProjectExplorer::SourceType, false));
                }
dt's avatar
dt committed
1005
1006
                m_processedUnits.insert(fileName);
            }
1007
            return;
dt's avatar
dt committed
1008
1009
        } else if (name() == "Option") {
            parseUnitOption();
1010
1011
1012
1013
1014
1015
        } else if (isStartElement()) {
            parseUnknownElement();
        }
    }
}

dt's avatar
dt committed
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
void CMakeCbpParser::parseUnitOption()
{
    if (attributes().hasAttribute("virtualFolder"))
        m_parsingCmakeUnit = true;

    while (!atEnd()) {
        readNext();

        if (isEndElement())
            break;

        if (isStartElement())
            parseUnknownElement();
    }
}

1032
1033
void CMakeCbpParser::parseUnknownElement()
{
dt's avatar
dt committed
1034
    Q_ASSERT(isStartElement());
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051

    while (!atEnd()) {
        readNext();

        if (isEndElement())
            break;

        if (isStartElement())
            parseUnknownElement();
    }
}

QList<ProjectExplorer::FileNode *> CMakeCbpParser::fileList()
{
    return m_fileList;
}

dt's avatar
dt committed
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
QList<ProjectExplorer::FileNode *> CMakeCbpParser::cmakeFileList()
{
    return m_cmakeFileList;
}

bool CMakeCbpParser::hasCMakeFiles()
{
    return !m_cmakeFileList.isEmpty();
}

1062
1063
1064
1065
QStringList CMakeCbpParser::includeFiles()
{
    return m_includeFiles;
}
dt's avatar
dt committed
1066
1067
1068
1069
1070
1071

QList<CMakeTarget> CMakeCbpParser::targets()
{
    return m_targets;
}

1072
1073
1074
1075
1076
QString CMakeCbpParser::compilerName() const
{
    return m_compiler;
}

dt's avatar
dt committed
1077
1078
1079
1080
1081
1082
1083
1084
1085
void CMakeTarget::clear()
{
    executable = QString::null;
    makeCommand = QString::null;
    makeCleanCommand = QString::null;
    workingDirectory = QString::null;
    title = QString::null;
}