cmakeopenprojectwizard.cpp 18 KB
Newer Older
1 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).
6
**
7
** Contact: Nokia Corporation (qt-info@nokia.com)
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
**
** Commercial Usage
**
** 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.
**
** GNU Lesser General Public License Usage
**
** 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.
**
** 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.
27 28 29
**
**************************************************************************/

30 31 32 33 34 35 36 37 38

/// TODO
/// To check
/// a) with an old cmake
/// => should not show combobox always use mingw generator
/// b) with an new cmake
/// always show combo box, defaulting if there's already a existing build


qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
39 40 41 42 43
#include "cmakeopenprojectwizard.h"
#include "cmakeprojectmanager.h"

#include <utils/pathchooser.h>
#include <projectexplorer/environment.h>
dt's avatar
dt committed
44
#include <projectexplorer/toolchain.h>
qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
45 46 47 48 49 50 51

#include <QtGui/QVBoxLayout>
#include <QtGui/QFormLayout>
#include <QtGui/QLabel>
#include <QtGui/QPushButton>
#include <QtGui/QPlainTextEdit>
#include <QtCore/QDateTime>
dt's avatar
dt committed
52
#include <QtCore/QStringList>
qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67

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

68
CMakeOpenProjectWizard::CMakeOpenProjectWizard(CMakeManager *cmakeManager, const QString &sourceDirectory, const ProjectExplorer::Environment &env)
qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
69
    : m_cmakeManager(cmakeManager),
dt's avatar
dt committed
70
      m_sourceDirectory(sourceDirectory),
71 72
      m_creatingCbpFiles(false),
      m_environment(env)
qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
73 74 75 76 77 78 79 80 81 82 83 84 85 86
{
    int startid;
    if (hasInSourceBuild()) {
        startid = InSourcePageId;
        m_buildDirectory = m_sourceDirectory;
    } else {
        startid = ShadowBuildPageId;
        m_buildDirectory = m_sourceDirectory + "/qtcreator-build";
    }

    setPage(InSourcePageId, new InSourceBuildPage(this));
    setPage(ShadowBuildPageId, new ShadowBuildPage(this));
    setPage(CMakeRunPageId, new CMakeRunPage(this));

87 88 89 90 91 92 93
    Utils::WizardProgress *wp = wizardProgress();
    Utils::WizardProgressItem *inSourceItem = wp->item(InSourcePageId);
    Utils::WizardProgressItem *shadowBuildItem = wp->item(ShadowBuildPageId);
    Utils::WizardProgressItem *cmakeRunItem = wp->item(CMakeRunPageId);
    inSourceItem->setNextItems(QList<Utils::WizardProgressItem *>() << cmakeRunItem);
    shadowBuildItem->setNextItems(QList<Utils::WizardProgressItem *>() << cmakeRunItem);

qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
94
    setStartId(startid);
dt's avatar
dt committed
95
    init();
qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
96 97
}

dt's avatar
dt committed
98
CMakeOpenProjectWizard::CMakeOpenProjectWizard(CMakeManager *cmakeManager, const QString &sourceDirectory,
99 100
                                               const QString &buildDirectory, CMakeOpenProjectWizard::Mode mode,
                                               const ProjectExplorer::Environment &env)
dt's avatar
dt committed
101 102
    : m_cmakeManager(cmakeManager),
      m_sourceDirectory(sourceDirectory),
103 104
      m_creatingCbpFiles(true),
      m_environment(env)
dt's avatar
dt committed
105
{
106
    if (mode == CMakeOpenProjectWizard::NeedToCreate)
107
        addPage(new CMakeRunPage(this, CMakeRunPage::Recreate, buildDirectory));
108
    else
109
        addPage(new CMakeRunPage(this, CMakeRunPage::Update, buildDirectory));
dt's avatar
dt committed
110
    init();
dt's avatar
dt committed
111 112
}

113
CMakeOpenProjectWizard::CMakeOpenProjectWizard(CMakeManager *cmakeManager, const QString &sourceDirectory,
114 115
                                               const QString &oldBuildDirectory,
                                               const ProjectExplorer::Environment &env)
116 117
    : m_cmakeManager(cmakeManager),
      m_sourceDirectory(sourceDirectory),
118 119
      m_creatingCbpFiles(true),
      m_environment(env)
120 121 122 123
{
    m_buildDirectory = oldBuildDirectory;
    addPage(new ShadowBuildPage(this, true));
    addPage(new CMakeRunPage(this, CMakeRunPage::Change));
dt's avatar
dt committed
124 125 126 127 128
    init();
}

void CMakeOpenProjectWizard::init()
{
129
    setOption(QWizard::NoBackButtonOnStartPage);
dt's avatar
dt committed
130
    setWindowTitle(tr("CMake Wizard"));
131 132
}

qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
133 134 135 136 137 138 139
CMakeManager *CMakeOpenProjectWizard::cmakeManager() const
{
    return m_cmakeManager;
}

int CMakeOpenProjectWizard::nextId() const
{
dt's avatar
dt committed
140 141
    if (m_creatingCbpFiles)
        return QWizard::nextId();
qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
142 143
    int cid = currentId();
    if (cid == InSourcePageId) {
144
        return CMakeRunPageId;
qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
145
    } else if (cid == ShadowBuildPageId) {
146
        return CMakeRunPageId;
qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
    }
    return -1;
}


bool CMakeOpenProjectWizard::hasInSourceBuild() const
{
    QFileInfo fi(m_sourceDirectory + "/CMakeCache.txt");
    if (fi.exists())
        return true;
    return false;
}

bool CMakeOpenProjectWizard::existsUpToDateXmlFile() const
{
    QString cbpFile = CMakeManager::findCbpFile(QDir(buildDirectory()));
    if (!cbpFile.isEmpty()) {
        // We already have a cbp file
        QFileInfo cbpFileInfo(cbpFile);
        QFileInfo cmakeListsFileInfo(sourceDirectory() + "/CMakeLists.txt");

        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;
}

dt's avatar
dt committed
189 190 191 192 193 194 195 196 197 198
QString CMakeOpenProjectWizard::msvcVersion() const
{
    return m_msvcVersion;
}

void CMakeOpenProjectWizard::setMsvcVersion(const QString &version)
{
    m_msvcVersion = version;
}

qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
199 200 201 202 203 204 205 206 207 208
QStringList CMakeOpenProjectWizard::arguments() const
{
    return m_arguments;
}

void CMakeOpenProjectWizard::setArguments(const QStringList &args)
{
    m_arguments = args;
}

209 210 211 212 213
ProjectExplorer::Environment CMakeOpenProjectWizard::environment() const
{
    return m_environment;
}

qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
214 215 216 217 218 219 220

InSourceBuildPage::InSourceBuildPage(CMakeOpenProjectWizard *cmakeWizard)
    : QWizardPage(cmakeWizard), m_cmakeWizard(cmakeWizard)
{
    setLayout(new QVBoxLayout);
    QLabel *label = new QLabel(this);
    label->setWordWrap(true);
221
    label->setText(tr("Qt Creator has detected an <b>in-source-build in %1</b> "
Friedemann Kleint's avatar
Friedemann Kleint committed
222
                   "which prevents shadow builds. Qt Creator will not allow you to change the build directory. "
223 224
                   "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
225
    layout()->addWidget(label);
226
    setTitle(tr("Build Location"));
qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
227 228 229
}


230
ShadowBuildPage::ShadowBuildPage(CMakeOpenProjectWizard *cmakeWizard, bool change)
qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
231 232 233 234 235 236 237
    : QWizardPage(cmakeWizard), m_cmakeWizard(cmakeWizard)
{
    QFormLayout *fl = new QFormLayout;
    this->setLayout(fl);

    QLabel *label = new QLabel(this);
    label->setWordWrap(true);
238 239 240 241 242 243 244
    if (change)
        label->setText(tr("Please enter the directory in which you want to build your project. "));
    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."));
qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
245
    fl->addWidget(label);
246
    m_pc = new Utils::PathChooser(this);
qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
247
    m_pc->setPath(m_cmakeWizard->buildDirectory());
con's avatar
con committed
248
    connect(m_pc, SIGNAL(changed(QString)), this, SLOT(buildDirectoryChanged()));
249
    fl->addRow(tr("Build directory:"), m_pc);
250
    setTitle(tr("Build Location"));
qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
251 252 253 254 255 256 257
}

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

258
CMakeRunPage::CMakeRunPage(CMakeOpenProjectWizard *cmakeWizard, Mode mode, const QString &buildDirectory)
dt's avatar
dt committed
259 260 261
    : QWizardPage(cmakeWizard),
      m_cmakeWizard(cmakeWizard),
      m_complete(false),
262 263
      m_mode(mode),
      m_buildDirectory(buildDirectory)
dt's avatar
dt committed
264 265 266 267 268
{
    initWidgets();
}

void CMakeRunPage::initWidgets()
qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
269 270 271
{
    QFormLayout *fl = new QFormLayout;
    setLayout(fl);
272
    // Description Label
dt's avatar
dt committed
273 274 275 276
    m_descriptionLabel = new QLabel(this);
    m_descriptionLabel->setWordWrap(true);

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

278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294
    if (m_cmakeWizard->cmakeManager()->isCMakeExecutableValid()) {
        m_cmakeExecutable = 0;
    } else {
        QString text = tr("Please specify the path to the cmake executable. No cmake executable was found in the path.");
        QString cmakeExecutable = m_cmakeWizard->cmakeManager()->cmakeExecutable();
        if (!cmakeExecutable.isEmpty()) {
            QFileInfo fi(cmakeExecutable);
            if (!fi.exists())
                text += tr(" The cmake executable (%1) does not exist.").arg(cmakeExecutable);
            else if (!fi.isExecutable())
                text += tr(" The path %1 is not a executable.").arg(cmakeExecutable);
            else
                text += tr(" The path %1 is not a valid cmake.").arg(cmakeExecutable);
        }

        fl->addRow(new QLabel(text, this));
        // Show a field for the user to enter
295 296
        m_cmakeExecutable = new Utils::PathChooser(this);
        m_cmakeExecutable->setExpectedKind(Utils::PathChooser::Command);
297 298 299 300
        fl->addRow("CMake Executable", m_cmakeExecutable);
    }

    // Run CMake Line (with arguments)
qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
301
    m_argumentsLineEdit = new QLineEdit(this);
302
    connect(m_argumentsLineEdit,SIGNAL(returnPressed()), this, SLOT(runCMake()));
qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
303

304
    m_generatorComboBox = new QComboBox(this);
dt's avatar
dt committed
305

qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
306
    m_runCMake = new QPushButton(this);
307
    m_runCMake->setText(tr("Run CMake"));
qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
308
    connect(m_runCMake, SIGNAL(clicked()), this, SLOT(runCMake()));
309 310 311

    QHBoxLayout *hbox = new QHBoxLayout;
    hbox->addWidget(m_argumentsLineEdit);
312
    hbox->addWidget(m_generatorComboBox);
313 314 315 316
    hbox->addWidget(m_runCMake);

    fl->addRow(tr("Arguments"), hbox);

317
    // Bottom output window
qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
318
    m_output = new QPlainTextEdit(this);
319
    m_output->setReadOnly(true);
320 321 322
    QSizePolicy pl = m_output->sizePolicy();
    pl.setVerticalStretch(1);
    m_output->setSizePolicy(pl);
qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
323
    fl->addRow(m_output);
324
    setTitle(tr("Run CMake"));
qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
325 326
}

327 328
void CMakeRunPage::initializePage()
{
329
    if (m_mode == Initial) {
330
        m_complete = m_cmakeWizard->existsUpToDateXmlFile();
331
        m_buildDirectory = m_cmakeWizard->buildDirectory();
332 333 334

        if (m_cmakeWizard->existsUpToDateXmlFile()) {
            m_descriptionLabel->setText(
335
                    tr("The directory %1 already contains a cbp file, which is recent enough. "
336 337 338 339 340 341 342
                       "You can pass special arguments or change the used toolchain here and rerun cmake. "
                       "Or simply finish the wizard directly").arg(m_buildDirectory));
        } else {
            m_descriptionLabel->setText(
                    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));
        }
343 344 345 346
    } else if (m_mode == CMakeRunPage::Update) {
        m_descriptionLabel->setText(tr("The directory %1 contains an outdated .cbp file. Qt "
                                       "Creator needs to update this file by running cmake. "
                                       "If you want to add additional command line arguments, "
Friedemann Kleint's avatar
Friedemann Kleint committed
347 348
                                       "add them below. Note that cmake remembers command "
                                       "line arguments from the previous runs.").arg(m_buildDirectory));
349
    } else if(m_mode == CMakeRunPage::Recreate) {
Friedemann Kleint's avatar
Friedemann Kleint committed
350
        m_descriptionLabel->setText(tr("The directory %1 specified in a build-configuration, "
351 352 353
                                       "does not contain a cbp file. Qt Creator needs to "
                                       "recreate this file, by running cmake. "
                                       "Some projects require command line arguments to "
Friedemann Kleint's avatar
Friedemann Kleint committed
354 355
                                       "the initial cmake call. Note that cmake remembers command "
                                       "line arguments from the previous runs.").arg(m_buildDirectory));
356 357 358 359 360
    } else if(m_mode == CMakeRunPage::Change) {
        m_buildDirectory = m_cmakeWizard->buildDirectory();
        m_descriptionLabel->setText(tr("Qt Creator needs to run cmake in the new build directory. "
                                       "Some projects require command line arguments to the "
                                       "initial cmake call."));
361
    }
362 363
    if (m_cmakeWizard->cmakeManager()->hasCodeBlocksMsvcGenerator()) {
        m_generatorComboBox->setVisible(true);
dt's avatar
dt committed
364
        QString cachedGenerator;
365
        // Try to find out generator from CMakeCachhe file, if it exists
366

367 368 369
        QFile fi(m_buildDirectory + "/CMakeCache.txt");
        if (fi.exists()) {
            // Cache exists, then read it...
370
            if (fi.open(QIODevice::ReadOnly | QIODevice::Text)) {
371
                while (!fi.atEnd()) {
372 373 374 375
                    QString line = fi.readLine();
                    if (line.startsWith("CMAKE_GENERATOR:INTERNAL=")) {
                        int splitpos = line.indexOf('=');
                        if (splitpos != -1) {
376
                            cachedGenerator = line.mid(splitpos + 1).trimmed();
377 378 379 380 381
                        }
                        break;
                    }
                }
            }
382
        }
dt's avatar
dt committed
383
        m_generatorComboBox->clear();
384
        // Find out whether we have multiple msvc versions
dt's avatar
dt committed
385 386 387 388 389 390 391 392
        QStringList msvcVersions = ProjectExplorer::ToolChain::availableMSVCVersions();
        if (msvcVersions.isEmpty()) {

        } else if (msvcVersions.count() == 1) {
            m_generatorComboBox->addItem(tr("NMake Generator"), msvcVersions.first());
        } else {
            foreach (const QString &msvcVersion, msvcVersions)
                m_generatorComboBox->addItem(tr("NMake Generator (%1)").arg(msvcVersion), msvcVersion);
393
        }
dt's avatar
dt committed
394

395
        if (cachedGenerator == "NMake Makefiles" && !msvcVersions.isEmpty()) {
dt's avatar
dt committed
396
            m_generatorComboBox->setCurrentIndex(0);
397 398
            m_cmakeWizard->setMsvcVersion(msvcVersions.first());
        }
dt's avatar
dt committed
399 400

        m_generatorComboBox->addItem(tr("MinGW Generator"), "mingw");
401
        if (cachedGenerator == "MinGW Makefiles") {
dt's avatar
dt committed
402
            m_generatorComboBox->setCurrentIndex(m_generatorComboBox->count() - 1);
403 404
            m_cmakeWizard->setMsvcVersion("");
        }
405 406 407 408
    } else {
        // No new enough cmake, simply hide the combo box
        m_generatorComboBox->setVisible(false);
    }
409 410
}

qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
411 412 413 414 415 416
void CMakeRunPage::runCMake()
{
    m_runCMake->setEnabled(false);
    m_argumentsLineEdit->setEnabled(false);
    QStringList arguments = ProjectExplorer::Environment::parseCombinedArgString(m_argumentsLineEdit->text());
    CMakeManager *cmakeManager = m_cmakeWizard->cmakeManager();
417 418

#ifdef Q_OS_WIN
dt's avatar
dt committed
419 420 421 422 423 424 425 426 427 428
    m_cmakeWizard->setMsvcVersion(QString());
    QString generator = QLatin1String("-GCodeBlocks - MinGW Makefiles");
    if (m_generatorComboBox->isVisible()) {
         // the combobox is shown, check which generator is selected
        int index = m_generatorComboBox->currentIndex();
        if (index != -1) {
            QString version = m_generatorComboBox->itemData(index).toString();
            if (version != "mingw") {
                generator = "-GCodeBlocks - NMake Makefiles";
                m_cmakeWizard->setMsvcVersion(version);
429 430
            } else {
                m_cmakeWizard->setMsvcVersion("");
dt's avatar
dt committed
431
            }
432
        }
dt's avatar
dt committed
433
    }
434
#else // Q_OS_WIN
dt's avatar
dt committed
435
    QString generator = QLatin1String("-GCodeBlocks - Unix Makefiles");
436
#endif
dt's avatar
dt committed
437 438 439 440 441 442 443 444
    ProjectExplorer::Environment env = m_cmakeWizard->environment();
    if (!m_cmakeWizard->msvcVersion().isEmpty()) {
        // Add the environment of that msvc version to environment
        ProjectExplorer::ToolChain *tc = ProjectExplorer::ToolChain::createMSVCToolChain(m_cmakeWizard->msvcVersion(), false);
        tc->addToEnvironment(env);
        delete tc;
    }

445 446 447 448 449 450 451 452
    if (m_cmakeExecutable) {
        // We asked the user for the cmake executable
        m_cmakeWizard->cmakeManager()->setCMakeExecutable(m_cmakeExecutable->path());
    }

    m_output->clear();

    if (m_cmakeWizard->cmakeManager()->isCMakeExecutableValid()) {
453
        m_cmakeProcess = new QProcess();
454 455
        connect(m_cmakeProcess, SIGNAL(readyRead()), this, SLOT(cmakeReadyRead()));
        connect(m_cmakeProcess, SIGNAL(finished(int)), this, SLOT(cmakeFinished()));
456
        cmakeManager->createXmlFile(m_cmakeProcess, arguments, m_cmakeWizard->sourceDirectory(), m_buildDirectory, env, generator);
457 458 459 460 461
    } else {
        m_runCMake->setEnabled(true);
        m_argumentsLineEdit->setEnabled(true);
        m_output->appendPlainText(tr("No valid cmake executable specified."));
    }
qtc-committer@nokia.com's avatar
qtc-committer@nokia.com committed
462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492
}

void CMakeRunPage::cmakeReadyRead()
{
    m_output->appendPlainText(m_cmakeProcess->readAll());
}

void CMakeRunPage::cmakeFinished()
{
    m_runCMake->setEnabled(true);
    m_argumentsLineEdit->setEnabled(true);
    m_cmakeProcess->deleteLater();
    m_cmakeProcess = 0;
    m_cmakeWizard->setArguments(ProjectExplorer::Environment::parseCombinedArgString(m_argumentsLineEdit->text()));
    //TODO Actually test that running cmake was finished, for setting this bool
    m_complete = true;
    emit completeChanged();
}

void CMakeRunPage::cleanupPage()
{
    m_output->clear();
    m_complete = false;
    emit completeChanged();
}

bool CMakeRunPage::isComplete() const
{
    return m_complete;
}