cmakeopenprojectwizard.cpp 25.9 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
2
**
3
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
hjk's avatar
hjk committed
4
** Contact: http://www.qt-project.org/legal
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
7
**
hjk's avatar
hjk committed
8
9
10
11
12
13
14
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
15
16
**
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17
18
19
20
21
22
23
24
25
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
con's avatar
con committed
26
27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
28
****************************************************************************/
29

qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
30
31
#include "cmakeopenprojectwizard.h"
#include "cmakeprojectmanager.h"
Peter Kümmel's avatar
Peter Kümmel committed
32
#include "cmakebuildconfiguration.h"
33
#include "cmakebuildinfo.h"
34
#include "generatorinfo.h"
qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
35

36
#include <coreplugin/icore.h>
37
#include <utils/hostosinfo.h>
qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
38
#include <utils/pathchooser.h>
hjk's avatar
hjk committed
39
#include <utils/fancylineedit.h>
40
#include <utils/historycompleter.h>
Tobias Hunger's avatar
Tobias Hunger committed
41
42
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/kitmanager.h>
43
44
#include <projectexplorer/toolchain.h>
#include <projectexplorer/abi.h>
45
#include <projectexplorer/projectexplorerconstants.h>
46
#include <texteditor/fontsettings.h>
47
#include <remotelinux/remotelinux_constants.h>
qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
48

49
50
51
52
53
54
#include <QVBoxLayout>
#include <QFormLayout>
#include <QLabel>
#include <QPushButton>
#include <QPlainTextEdit>
#include <QDateTime>
55
#include <QSettings>
56
#include <QStringList>
57
#include <QApplication>
qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72

using namespace CMakeProjectManager;
using namespace CMakeProjectManager::Internal;

///////
//  Page Flow:
//   Start (No .user file)
//    |
//    |---> In Source Build --> Page: Tell the user about that
//                               |--> Already existing cbp file (and new enough) --> Page: Ready to load the project
//                               |--> Page: Ask for cmd options, run generator
//    |---> No in source Build --> Page: Ask the user for the build directory
//                                   |--> Already existing cbp file (and new enough) --> Page: Ready to load the project
//                                   |--> Page: Ask for cmd options, run generator

Peter Kümmel's avatar
Peter Kümmel committed
73

74
75
76
//////////////
/// CMakeOpenProjectWizard
//////////////
Peter Kümmel's avatar
Peter Kümmel committed
77

78
79
80
CMakeOpenProjectWizard::CMakeOpenProjectWizard(QWidget *parent, CMakeManager *cmakeManager, const QString &sourceDirectory, Utils::Environment env)
    : Utils::Wizard(parent),
      m_cmakeManager(cmakeManager),
dt's avatar
dt committed
81
      m_sourceDirectory(sourceDirectory),
82
83
84
      m_environment(env),
      m_useNinja(false),
      m_kit(0)
qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
85
{
86
87
88
    if (!compatibleKitExist())
        addPage(new NoKitPage(this));

qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
89
90
    if (hasInSourceBuild()) {
        m_buildDirectory = m_sourceDirectory;
91
        addPage(new InSourceBuildPage(this));
qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
92
    } else {
93
        m_buildDirectory = m_sourceDirectory + QLatin1String("-build");
94
        addPage(new ShadowBuildPage(this));
qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
95
96
    }

97
98
    if (!m_cmakeManager->isCMakeExecutableValid())
        addPage(new ChooseCMakePage(this));
qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
99

100
    addPage(new CMakeRunPage(this));
101

dt's avatar
dt committed
102
    init();
qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
103
104
}

105
CMakeOpenProjectWizard::CMakeOpenProjectWizard(QWidget *parent, CMakeManager *cmakeManager, CMakeOpenProjectWizard::Mode mode,
106
                                               const CMakeBuildInfo *info)
107
108
    : Utils::Wizard(parent),
      m_cmakeManager(cmakeManager),
109
110
111
112
      m_sourceDirectory(info->sourceDirectory),
      m_environment(info->environment),
      m_useNinja(info->useNinja),
      m_kit(0)
dt's avatar
dt committed
113
{
114
115
    m_kit = ProjectExplorer::KitManager::find(info->kitId);

116
    CMakeRunPage::Mode rmode;
117
    if (mode == CMakeOpenProjectWizard::NeedToCreate)
118
119
120
        rmode = CMakeRunPage::Recreate;
    else if (mode == CMakeOpenProjectWizard::WantToUpdate)
        rmode = CMakeRunPage::WantToUpdate;
121
    else if (mode == CMakeOpenProjectWizard::NeedToUpdate)
122
        rmode = CMakeRunPage::NeedToUpdate;
123
124
    else
        rmode = CMakeRunPage::ChangeDirectory;
dt's avatar
dt committed
125

126
    if (mode == CMakeOpenProjectWizard::ChangeDirectory) {
127
        m_buildDirectory = info->buildDirectory.toString();
128
129
        addPage(new ShadowBuildPage(this, true));
    }
130
131
    if (!m_cmakeManager->isCMakeExecutableValid())
        addPage(new ChooseCMakePage(this));
132

133
    addPage(new CMakeRunPage(this, rmode, info->buildDirectory.toString()));
dt's avatar
dt committed
134
135
136
137
138
139
    init();
}

void CMakeOpenProjectWizard::init()
{
    setWindowTitle(tr("CMake Wizard"));
140
141
}

qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
142
143
144
145
146
147
148
CMakeManager *CMakeOpenProjectWizard::cmakeManager() const
{
    return m_cmakeManager;
}

bool CMakeOpenProjectWizard::hasInSourceBuild() const
{
149
    QFileInfo fi(m_sourceDirectory + QLatin1String("/CMakeCache.txt"));
qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
150
151
152
153
154
    if (fi.exists())
        return true;
    return false;
}

155
156
157
158
bool CMakeOpenProjectWizard::compatibleKitExist() const
{
    bool hasCodeBlocksGenerator = m_cmakeManager->hasCodeBlocksMsvcGenerator();
    bool hasNinjaGenerator = m_cmakeManager->hasCodeBlocksNinjaGenerator();
159
    bool preferNinja = m_cmakeManager->preferNinja();
160

161
    QList<ProjectExplorer::Kit *> kitList = ProjectExplorer::KitManager::kits();
162
163
164
165
166
167
168

    foreach (ProjectExplorer::Kit *k, kitList) {
        // OfferNinja and ForceNinja differ in what they return
        // but not whether the list is empty or not, which is what we
        // are interested in here
        QList<GeneratorInfo> infos = GeneratorInfo::generatorInfosFor(k,
                                                                      hasNinjaGenerator ? GeneratorInfo::OfferNinja : GeneratorInfo::NoNinja,
169
                                                                      preferNinja,
170
171
172
173
174
175
176
                                                                      hasCodeBlocksGenerator);
        if (!infos.isEmpty())
            return true;
    }
    return false;
}

qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
177
178
179
180
181
182
bool CMakeOpenProjectWizard::existsUpToDateXmlFile() const
{
    QString cbpFile = CMakeManager::findCbpFile(QDir(buildDirectory()));
    if (!cbpFile.isEmpty()) {
        // We already have a cbp file
        QFileInfo cbpFileInfo(cbpFile);
183
        QFileInfo cmakeListsFileInfo(sourceDirectory() + QLatin1String("/CMakeLists.txt"));
qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205

        if (cbpFileInfo.lastModified() > cmakeListsFileInfo.lastModified())
            return true;
    }
    return false;
}

QString CMakeOpenProjectWizard::buildDirectory() const
{
    return m_buildDirectory;
}

QString CMakeOpenProjectWizard::sourceDirectory() const
{
    return m_sourceDirectory;
}

void CMakeOpenProjectWizard::setBuildDirectory(const QString &directory)
{
    m_buildDirectory = directory;
}

206
207
208
209
210
211
212
213
214
215
bool CMakeOpenProjectWizard::useNinja() const
{
    return m_useNinja;
}

void CMakeOpenProjectWizard::setUseNinja(bool b)
{
    m_useNinja = b;
}

216
QString CMakeOpenProjectWizard::arguments() const
dt's avatar
dt committed
217
{
218
    return m_arguments;
dt's avatar
dt committed
219
220
}

221
void CMakeOpenProjectWizard::setArguments(const QString &args)
dt's avatar
dt committed
222
{
223
    m_arguments = args;
dt's avatar
dt committed
224
225
}

226
Utils::Environment CMakeOpenProjectWizard::environment() const
227
{
228
229
230
231
232
233
    return m_environment;
}

ProjectExplorer::Kit *CMakeOpenProjectWizard::kit() const
{
    return m_kit;
234
235
}

236
void CMakeOpenProjectWizard::setKit(ProjectExplorer::Kit *kit)
Peter Kümmel's avatar
Peter Kümmel committed
237
{
238
    m_kit = kit;
Peter Kümmel's avatar
Peter Kümmel committed
239
}
qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
240

241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
//////
// NoKitPage
/////

NoKitPage::NoKitPage(CMakeOpenProjectWizard *cmakeWizard)
    : QWizardPage(cmakeWizard), m_cmakeWizard(cmakeWizard)
{
    QVBoxLayout *layout = new QVBoxLayout;
    setLayout(layout);

    m_descriptionLabel = new QLabel(this);
    m_descriptionLabel->setWordWrap(true);
    layout->addWidget(m_descriptionLabel);

    m_optionsButton = new QPushButton;
256
    m_optionsButton->setText(Core::ICore::msgShowOptionsDialog());
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297

    connect(m_optionsButton, SIGNAL(clicked()),
            this, SLOT(showOptions()));

    QHBoxLayout *hbox = new QHBoxLayout;
    hbox->addWidget(m_optionsButton);
    hbox->addStretch();

    layout->addLayout(hbox);

    setTitle(tr("Check Kits"));

    connect(ProjectExplorer::KitManager::instance(), SIGNAL(kitsChanged()),
            this, SLOT(kitsChanged()));

    kitsChanged();
}

void NoKitPage::kitsChanged()
{
    if (isComplete()) {
        m_descriptionLabel->setText(tr("There are compatible kits."));
        m_optionsButton->setVisible(false);
    } else {
        m_descriptionLabel->setText(tr("Qt Creator has no kits that are suitable for CMake projects. Please configure a kit."));
        m_optionsButton->setVisible(true);
    }
    emit completeChanged();
}

bool NoKitPage::isComplete() const
{
    return m_cmakeWizard->compatibleKitExist();
}

void NoKitPage::showOptions()
{
    Core::ICore::showOptionsDialog(Core::Id(ProjectExplorer::Constants::PROJECTEXPLORER_SETTINGS_CATEGORY),
                                   Core::Id(ProjectExplorer::Constants::KITS_SETTINGS_PAGE_ID), this);
}

qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
298
299
300
301
302
303
InSourceBuildPage::InSourceBuildPage(CMakeOpenProjectWizard *cmakeWizard)
    : QWizardPage(cmakeWizard), m_cmakeWizard(cmakeWizard)
{
    setLayout(new QVBoxLayout);
    QLabel *label = new QLabel(this);
    label->setWordWrap(true);
304
    label->setText(tr("Qt Creator has detected an <b>in-source-build in %1</b> "
Friedemann Kleint's avatar
Friedemann Kleint committed
305
                   "which prevents shadow builds. Qt Creator will not allow you to change the build directory. "
306
307
                   "If you want a shadow build, clean your source directory and re-open the project.")
                   .arg(m_cmakeWizard->buildDirectory()));
qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
308
    layout()->addWidget(label);
309
    setTitle(tr("Build Location"));
qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
310
311
}

312
ShadowBuildPage::ShadowBuildPage(CMakeOpenProjectWizard *cmakeWizard, bool change)
qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
313
314
315
316
317
318
319
    : QWizardPage(cmakeWizard), m_cmakeWizard(cmakeWizard)
{
    QFormLayout *fl = new QFormLayout;
    this->setLayout(fl);

    QLabel *label = new QLabel(this);
    label->setWordWrap(true);
320
    if (change)
321
        label->setText(tr("Please enter the directory in which you want to build your project.") + QLatin1Char(' '));
322
323
324
325
326
    else
        label->setText(tr("Please enter the directory in which you want to build your project. "
                          "Qt Creator recommends to not use the source directory for building. "
                          "This ensures that the source directory remains clean and enables multiple builds "
                          "with different settings."));
con's avatar
con committed
327
    fl->addRow(label);
328
    m_pc = new Utils::PathChooser(this);
329
    m_pc->setBaseDirectory(m_cmakeWizard->sourceDirectory());
qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
330
    m_pc->setPath(m_cmakeWizard->buildDirectory());
331
    m_pc->setExpectedKind(Utils::PathChooser::Directory);
332
    m_pc->setHistoryCompleter(QLatin1String("Cmake.BuildDir.History"));
con's avatar
con committed
333
    connect(m_pc, SIGNAL(changed(QString)), this, SLOT(buildDirectoryChanged()));
334
    fl->addRow(tr("Build directory:"), m_pc);
335
    setTitle(tr("Build Location"));
qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
336
337
338
339
340
341
342
}

void ShadowBuildPage::buildDirectoryChanged()
{
    m_cmakeWizard->setBuildDirectory(m_pc->path());
}

343
344
345
346
347
348
349
350
351
352
353
354
355
ChooseCMakePage::ChooseCMakePage(CMakeOpenProjectWizard *cmakeWizard)
    : QWizardPage(cmakeWizard), m_cmakeWizard(cmakeWizard)
{
    QFormLayout *fl = new QFormLayout;
    fl->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow);
    setLayout(fl);

    m_cmakeLabel = new QLabel;
    m_cmakeLabel->setWordWrap(true);
    fl->addRow(m_cmakeLabel);
    // Show a field for the user to enter
    m_cmakeExecutable = new Utils::PathChooser(this);
    m_cmakeExecutable->setExpectedKind(Utils::PathChooser::ExistingCommand);
356
    m_cmakeExecutable->setHistoryCompleter(QLatin1String("Cmake.Command.History"));
357
    fl->addRow(tr("CMake Executable:"), m_cmakeExecutable);
358
359
360
361
362
363

    connect(m_cmakeExecutable, SIGNAL(editingFinished()),
            this, SLOT(cmakeExecutableChanged()));
    connect(m_cmakeExecutable, SIGNAL(browsingFinished()),
            this, SLOT(cmakeExecutableChanged()));

364
    setTitle(tr("Choose CMake Executable"));
365
366
367
368
369
370
}

void ChooseCMakePage::updateErrorText()
{
    QString cmakeExecutable = m_cmakeWizard->cmakeManager()->cmakeExecutable();
    if (m_cmakeWizard->cmakeManager()->isCMakeExecutableValid()) {
371
        m_cmakeLabel->setText(tr("The CMake executable is valid."));
372
    } else {
373
        QString text = tr("Specify the path to the CMake executable. No CMake executable was found in the path.");
374
        if (!cmakeExecutable.isEmpty()) {
375
            text += QLatin1Char(' ');
376
377
            QFileInfo fi(cmakeExecutable);
            if (!fi.exists())
378
                text += tr("The CMake executable (%1) does not exist.").arg(cmakeExecutable);
379
            else if (!fi.isExecutable())
380
                text += tr("The path %1 is not an executable.").arg(cmakeExecutable);
381
            else
382
                text += tr("The path %1 is not a valid CMake executable.").arg(cmakeExecutable);
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
        }
        m_cmakeLabel->setText(text);
    }
}

void ChooseCMakePage::cmakeExecutableChanged()
{
    m_cmakeWizard->cmakeManager()->setCMakeExecutable(m_cmakeExecutable->path());
    updateErrorText();
    emit completeChanged();
}

bool ChooseCMakePage::isComplete() const
{
    return m_cmakeWizard->cmakeManager()->isCMakeExecutableValid();
}

400
CMakeRunPage::CMakeRunPage(CMakeOpenProjectWizard *cmakeWizard, Mode mode, const QString &buildDirectory)
dt's avatar
dt committed
401
402
    : QWizardPage(cmakeWizard),
      m_cmakeWizard(cmakeWizard),
403
      m_haveCbpFile(false),
404
405
      m_mode(mode),
      m_buildDirectory(buildDirectory)
dt's avatar
dt committed
406
407
408
409
410
{
    initWidgets();
}

void CMakeRunPage::initWidgets()
qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
411
412
{
    QFormLayout *fl = new QFormLayout;
con's avatar
con committed
413
    fl->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow);
qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
414
    setLayout(fl);
415
    // Description Label
dt's avatar
dt committed
416
417
418
419
    m_descriptionLabel = new QLabel(this);
    m_descriptionLabel->setWordWrap(true);

    fl->addRow(m_descriptionLabel);
qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
420

421
    // Run CMake Line (with arguments)
hjk's avatar
hjk committed
422
    m_argumentsLineEdit = new Utils::FancyLineEdit(this);
423
    m_argumentsLineEdit->setHistoryCompleter(QLatin1String("CMakeArgumentsLineEdit"));
424
    m_argumentsLineEdit->selectAll();
425

426
    connect(m_argumentsLineEdit,SIGNAL(returnPressed()), this, SLOT(runCMake()));
con's avatar
con committed
427
    fl->addRow(tr("Arguments:"), m_argumentsLineEdit);
qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
428

429
    m_generatorComboBox = new QComboBox(this);
con's avatar
con committed
430
    fl->addRow(tr("Generator:"), m_generatorComboBox);
dt's avatar
dt committed
431

432
433
434
    m_generatorExtraText = new QLabel(this);
    fl->addRow(m_generatorExtraText);

qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
435
    m_runCMake = new QPushButton(this);
436
    m_runCMake->setText(tr("Run CMake"));
qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
437
    connect(m_runCMake, SIGNAL(clicked()), this, SLOT(runCMake()));
438

con's avatar
con committed
439
440
441
442
    QHBoxLayout *hbox2 = new QHBoxLayout;
    hbox2->addStretch(10);
    hbox2->addWidget(m_runCMake);
    fl->addRow(hbox2);
443

444
    // Bottom output window
qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
445
    m_output = new QPlainTextEdit(this);
446
    m_output->setReadOnly(true);
con's avatar
con committed
447
448
449
    // set smaller minimum size to avoid vanishing descriptions if all of the
    // above is shown and the dialog not vertically resizing to fit stuff in (Mac)
    m_output->setMinimumHeight(15);
450
    QFont f(TextEditor::FontSettings::defaultFixedFontFamily());
451
452
    f.setStyleHint(QFont::TypeWriter);
    m_output->setFont(f);
453
454
455
    QSizePolicy pl = m_output->sizePolicy();
    pl.setVerticalStretch(1);
    m_output->setSizePolicy(pl);
qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
456
    fl->addRow(m_output);
457
458
459
460
461

    m_exitCodeLabel = new QLabel(this);
    m_exitCodeLabel->setVisible(false);
    fl->addRow(m_exitCodeLabel);

462
    setTitle(tr("Run CMake"));
463
    setMinimumSize(600, 400);
qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
464
465
}

466
QByteArray CMakeRunPage::cachedGeneratorFromFile(const QString &cache)
467
468
469
470
471
472
{
    QFile fi(cache);
    if (fi.exists()) {
        // Cache exists, then read it...
        if (fi.open(QIODevice::ReadOnly | QIODevice::Text)) {
            while (!fi.atEnd()) {
473
                QByteArray line = fi.readLine();
474
475
476
                if (line.startsWith("CMAKE_GENERATOR:INTERNAL=")) {
                    int splitpos = line.indexOf('=');
                    if (splitpos != -1) {
477
                        QByteArray cachedGenerator = line.mid(splitpos + 1).trimmed();
478
479
480
481
482
483
484
                        if (!cachedGenerator.isEmpty())
                            return cachedGenerator;
                    }
                }
            }
        }
    }
485
    return QByteArray();
486
487
}

488
489
void CMakeRunPage::initializePage()
{
490
    if (m_mode == Initial) {
Nikolai Kosjar's avatar
Nikolai Kosjar committed
491
        bool upToDateXmlFile = m_cmakeWizard->existsUpToDateXmlFile();
492
        m_buildDirectory = m_cmakeWizard->buildDirectory();
493

494
        if (upToDateXmlFile) {
495
            m_descriptionLabel->setText(
496
                    tr("The directory %1 already contains a cbp file, which is recent enough. "
497
                       "You can pass special arguments and rerun CMake. "
498
                       "Or simply finish the wizard directly.").arg(m_buildDirectory));
499
            m_haveCbpFile = true;
500
501
        } else {
            m_descriptionLabel->setText(
502
503
                    tr("The directory %1 does not contain a cbp file. Qt Creator needs to create this file by running CMake. "
                       "Some projects require command line arguments to the initial CMake call.").arg(m_buildDirectory));
504
        }
505
    } else if (m_mode == CMakeRunPage::NeedToUpdate) {
506
        m_descriptionLabel->setText(tr("The directory %1 contains an outdated .cbp file. Qt "
507
                                       "Creator needs to update this file by running CMake. "
508
                                       "If you want to add additional command line arguments, "
509
                                       "add them below. Note that CMake remembers command "
Friedemann Kleint's avatar
Friedemann Kleint committed
510
                                       "line arguments from the previous runs.").arg(m_buildDirectory));
tomdeblauwe's avatar
tomdeblauwe committed
511
    } else if (m_mode == CMakeRunPage::Recreate) {
Friedemann Kleint's avatar
Friedemann Kleint committed
512
        m_descriptionLabel->setText(tr("The directory %1 specified in a build-configuration, "
513
                                       "does not contain a cbp file. Qt Creator needs to "
514
                                       "recreate this file, by running CMake. "
515
                                       "Some projects require command line arguments to "
516
                                       "the initial CMake call. Note that CMake remembers command "
Friedemann Kleint's avatar
Friedemann Kleint committed
517
                                       "line arguments from the previous runs.").arg(m_buildDirectory));
tomdeblauwe's avatar
tomdeblauwe committed
518
    } else if (m_mode == CMakeRunPage::ChangeDirectory) {
519
        m_buildDirectory = m_cmakeWizard->buildDirectory();
520
        m_descriptionLabel->setText(tr("Qt Creator needs to run CMake in the new build directory. "
521
                                       "Some projects require command line arguments to the "
522
                                       "initial CMake call."));
523
524
    } else if (m_mode == CMakeRunPage::WantToUpdate) {
        m_descriptionLabel->setText(tr("Refreshing cbp file in %1.").arg(m_buildDirectory));
525
    }
526
527
528
529

    // Build the list of generators/toolchains we want to offer
    m_generatorComboBox->clear();

530
531
    bool hasCodeBlocksGenerator = m_cmakeWizard->cmakeManager()->hasCodeBlocksMsvcGenerator();
    bool hasNinjaGenerator = m_cmakeWizard->cmakeManager()->hasCodeBlocksNinjaGenerator();
532
    bool preferNinja = m_cmakeWizard->cmakeManager()->preferNinja();
Tobias Hunger's avatar
Tobias Hunger committed
533

534
535
    if (m_mode == Initial) {
        // Try figuring out generator and toolchain from CMakeCache.txt
536
        QByteArray cachedGenerator = cachedGeneratorFromFile(m_buildDirectory + QLatin1String("/CMakeCache.txt"));
537
538

        m_generatorComboBox->show();
539
        QList<ProjectExplorer::Kit *> kitList = ProjectExplorer::KitManager::kits();
540
        int defaultIndex = 0;
541
542
543
544

        foreach (ProjectExplorer::Kit *k, kitList) {
            QList<GeneratorInfo> infos = GeneratorInfo::generatorInfosFor(k,
                                                                          hasNinjaGenerator ? GeneratorInfo::OfferNinja : GeneratorInfo::NoNinja,
545
                                                                          preferNinja,
546
547
                                                                          hasCodeBlocksGenerator);

548
            if (k == ProjectExplorer::KitManager::defaultKit())
549
550
                defaultIndex = m_generatorComboBox->count();

551
552
553
            foreach (const GeneratorInfo &info, infos)
                if (cachedGenerator.isEmpty() || info.generator() == cachedGenerator)
                    m_generatorComboBox->addItem(info.displayName(), qVariantFromValue(info));
554
        }
555

556
557
558
559
560
561
562
563
564

        if (!m_generatorComboBox->count()) {
            m_generatorExtraText->setVisible(true);
            m_generatorExtraText->setText(tr("The cached generator %1 is incompatible with the configured kits.")
                                          .arg(QString::fromLatin1(cachedGenerator)));
        } else {
            m_generatorExtraText->setVisible(false);
        }

565
        m_generatorComboBox->setCurrentIndex(defaultIndex);
566
567
568
569
570
571
572
573
574
575
576
    } else {
        // Note: We don't compare the actually cached generator to what is set in the buildconfiguration
        // We assume that the buildconfiguration is correct
        GeneratorInfo::Ninja ninja;
        if (m_mode == CMakeRunPage::NeedToUpdate || m_mode == CMakeRunPage::WantToUpdate) {
            ninja = m_cmakeWizard->useNinja() ? GeneratorInfo::ForceNinja : GeneratorInfo::NoNinja;
        } else { // Recreate, ChangeDirectory
            // Note: ReCreate is technically just a removed .cbp file, we assume the cache
            // got removed too. If the cache still exists the error message from cmake should
            // be a good hint to change the generator
            ninja = hasNinjaGenerator ? GeneratorInfo::OfferNinja : GeneratorInfo::NoNinja;
577
        }
578
579
580

        QList<GeneratorInfo> infos = GeneratorInfo::generatorInfosFor(m_cmakeWizard->kit(),
                                                                      ninja,
581
                                                                      preferNinja,
582
583
                                                                      true);
        foreach (const GeneratorInfo &info, infos)
584
            m_generatorComboBox->addItem(info.displayName(), qVariantFromValue(info));
585
586
587
588
589
    }
}

bool CMakeRunPage::validatePage()
{
590
591
592
593
594
595
    int index = m_generatorComboBox->currentIndex();
    if (index == -1)
        return false;
    GeneratorInfo generatorInfo = m_generatorComboBox->itemData(index).value<GeneratorInfo>();
    m_cmakeWizard->setKit(generatorInfo.kit());
    m_cmakeWizard->setUseNinja(generatorInfo.isNinja());
596
    return QWizardPage::validatePage();
597
598
}

qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
599
600
void CMakeRunPage::runCMake()
{
601
    m_haveCbpFile = false;
602
603

    Utils::Environment env = m_cmakeWizard->environment();
604
605
    int index = m_generatorComboBox->currentIndex();

606
    if (index == -1) {
607
608
609
        m_output->appendPlainText(tr("No generator selected."));
        return;
    }
610
611
612
    GeneratorInfo generatorInfo = m_generatorComboBox->itemData(index).value<GeneratorInfo>();
    m_cmakeWizard->setKit(generatorInfo.kit());
    m_cmakeWizard->setUseNinja(generatorInfo.isNinja());
613

Sergio Ahumada's avatar
Sergio Ahumada committed
614
    // If mode is initial the user chooses the kit, otherwise it's already chosen
615
616
617
    // and the environment already contains the kit
    if (m_mode == Initial)
        generatorInfo.kit()->addToEnvironment(env);
618

qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
619
620
    m_runCMake->setEnabled(false);
    m_argumentsLineEdit->setEnabled(false);
621
622
    m_generatorComboBox->setEnabled(false);

623
624
    m_output->clear();

625
    CMakeManager *cmakeManager = m_cmakeWizard->cmakeManager();
626
    if (m_cmakeWizard->cmakeManager()->isCMakeExecutableValid()) {
627
        m_cmakeProcess = new Utils::QtcProcess();
628
629
        connect(m_cmakeProcess, SIGNAL(readyReadStandardOutput()), this, SLOT(cmakeReadyReadStandardOutput()));
        connect(m_cmakeProcess, SIGNAL(readyReadStandardError()), this, SLOT(cmakeReadyReadStandardError()));
630
        connect(m_cmakeProcess, SIGNAL(finished(int)), this, SLOT(cmakeFinished()));
631
        cmakeManager->createXmlFile(m_cmakeProcess, m_argumentsLineEdit->text(), m_cmakeWizard->sourceDirectory(),
632
                                    m_buildDirectory, env, QString::fromLatin1(generatorInfo.generatorArgument()));
633
634
635
    } else {
        m_runCMake->setEnabled(true);
        m_argumentsLineEdit->setEnabled(true);
636
        m_generatorComboBox->setEnabled(true);
637
        m_output->appendPlainText(tr("No valid CMake executable specified."));
638
    }
qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
639
640
}

641
static QColor mix_colors(const QColor &a, const QColor &b)
642
643
644
645
646
647
648
649
{
    return QColor((a.red() + 2 * b.red()) / 3, (a.green() + 2 * b.green()) / 3,
                  (a.blue() + 2* b.blue()) / 3, (a.alpha() + 2 * b.alpha()) / 3);
}

void CMakeRunPage::cmakeReadyReadStandardOutput()
{
    QTextCursor cursor(m_output->document());
dt_'s avatar
dt_ committed
650
    cursor.movePosition(QTextCursor::End);
651
652
653
654
655
656
    QTextCharFormat tf;

    QFont font = m_output->font();
    tf.setFont(font);
    tf.setForeground(m_output->palette().color(QPalette::Text));

657
    cursor.insertText(QString::fromLocal8Bit(m_cmakeProcess->readAllStandardOutput()), tf);
658
659
660
}

void CMakeRunPage::cmakeReadyReadStandardError()
qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
661
{
662
663
664
665
666
667
668
669
670
    QTextCursor cursor(m_output->document());
    QTextCharFormat tf;

    QFont font = m_output->font();
    QFont boldFont = font;
    boldFont.setBold(true);
    tf.setFont(boldFont);
    tf.setForeground(mix_colors(m_output->palette().color(QPalette::Text), QColor(Qt::red)));

671
    cursor.insertText(QString::fromLocal8Bit(m_cmakeProcess->readAllStandardError()), tf);
qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
672
673
674
675
676
677
}

void CMakeRunPage::cmakeFinished()
{
    m_runCMake->setEnabled(true);
    m_argumentsLineEdit->setEnabled(true);
678
679
    m_generatorComboBox->setEnabled(true);

680
681
    if (m_cmakeProcess->exitCode() != 0) {
        m_exitCodeLabel->setVisible(true);
Jarek Kobus's avatar
Jarek Kobus committed
682
        m_exitCodeLabel->setText(tr("CMake exited with errors. Please check CMake output."));
683
        static_cast<Utils::HistoryCompleter *>(m_argumentsLineEdit->completer())->removeHistoryItem(0);
684
        m_haveCbpFile = false;
685
686
    } else {
        m_exitCodeLabel->setVisible(false);
687
        m_haveCbpFile = true;
688
    }
qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
689
690
    m_cmakeProcess->deleteLater();
    m_cmakeProcess = 0;
691
    m_cmakeWizard->setArguments(m_argumentsLineEdit->text());
qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
692
693
694
695
696
697
    emit completeChanged();
}

void CMakeRunPage::cleanupPage()
{
    m_output->clear();
698
    m_haveCbpFile = false;
699
    m_exitCodeLabel->setVisible(false);
qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
700
701
702
703
704
    emit completeChanged();
}

bool CMakeRunPage::isComplete() const
{
705
706
    int index = m_generatorComboBox->currentIndex();
    return index != -1 && m_haveCbpFile;
qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
707
}