cmakeprojectmanager.cpp 15.4 KB
Newer Older
1
/**************************************************************************
con's avatar
con committed
2
3
4
**
** This file is part of Qt Creator
**
con's avatar
con committed
5
** Copyright (c) 2011 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
**
10
** GNU Lesser General Public License Usage
11
**
hjk's avatar
hjk committed
12
13
14
15
16
17
** 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.
18
**
con's avatar
con committed
19
** In addition, as a special exception, Nokia gives you certain additional
hjk's avatar
hjk committed
20
** rights. These rights are described in the Nokia Qt LGPL Exception
con's avatar
con committed
21
22
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
23
24
25
26
27
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
con's avatar
con committed
28
** If you have questions regarding the use of this file, please contact
29
** Nokia at qt-info@nokia.com.
con's avatar
con committed
30
**
31
**************************************************************************/
hjk's avatar
hjk committed
32

33
#include "cmakeopenprojectwizard.h"
con's avatar
con committed
34
35
36
37
#include "cmakeprojectmanager.h"
#include "cmakeprojectconstants.h"
#include "cmakeproject.h"

38
#include <utils/synchronousprocess.h>
39
#include <utils/qtcprocess.h>
40

dt's avatar
dt committed
41
#include <coreplugin/icore.h>
42
#include <coreplugin/id.h>
43
44
45
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/actionmanager/command.h>
#include <coreplugin/actionmanager/actioncontainer.h>
con's avatar
con committed
46
#include <projectexplorer/projectexplorerconstants.h>
47
#include <projectexplorer/projectexplorer.h>
dt's avatar
dt committed
48
49
#include <qtconcurrent/QtConcurrentTools>
#include <QtCore/QtConcurrentRun>
50
#include <QtCore/QCoreApplication>
51
#include <QtCore/QSettings>
52
#include <QtCore/QDateTime>
dt's avatar
dt committed
53
#include <QtGui/QFormLayout>
54
#include <QtGui/QBoxLayout>
55
56
#include <QtGui/QDesktopServices>
#include <QtGui/QApplication>
57
#include <QtGui/QLabel>
58
59
#include <QtGui/QGroupBox>
#include <QtGui/QSpacerItem>
con's avatar
con committed
60
61
62

using namespace CMakeProjectManager::Internal;

63
64
CMakeManager::CMakeManager(CMakeSettingsPage *cmakeSettingsPage)
    : m_settingsPage(cmakeSettingsPage)
con's avatar
con committed
65
{
66
67
68
69
70
71
72
73
74
75
76
77
78
    ProjectExplorer::ProjectExplorerPlugin *projectExplorer = ProjectExplorer::ProjectExplorerPlugin::instance();
    connect(projectExplorer, SIGNAL(aboutToShowContextMenu(ProjectExplorer::Project*, ProjectExplorer::Node*)),
            this, SLOT(updateContextMenu(ProjectExplorer::Project*, ProjectExplorer::Node*)));

    Core::ActionManager *am = Core::ICore::instance()->actionManager();

    Core::ActionContainer *mbuild =
            am->actionContainer(ProjectExplorer::Constants::M_BUILDPROJECT);
    Core::ActionContainer *mproject =
            am->actionContainer(ProjectExplorer::Constants::M_PROJECTCONTEXT);
    Core::ActionContainer *msubproject =
            am->actionContainer(ProjectExplorer::Constants::M_SUBPROJECTCONTEXT);

79
80
    const Core::Context projectContext(CMakeProjectManager::Constants::PROJECTCONTEXT);

Jarek Kobus's avatar
Jarek Kobus committed
81
    m_runCMakeAction = new QAction(QIcon(), tr("Run CMake"), this);
82
    Core::Command *command = am->registerAction(m_runCMakeAction, Constants::RUNCMAKE, projectContext);
83
84
85
86
    command->setAttribute(Core::Command::CA_Hide);
    mbuild->addAction(command, ProjectExplorer::Constants::G_BUILD_PROJECT);
    connect(m_runCMakeAction, SIGNAL(triggered()), this, SLOT(runCMake()));

Jarek Kobus's avatar
Jarek Kobus committed
87
    m_runCMakeActionContextMenu = new QAction(QIcon(), tr("Run CMake"), this);
88
    command = am->registerAction(m_runCMakeActionContextMenu, Constants::RUNCMAKECONTEXTMENU, projectContext);
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
    command->setAttribute(Core::Command::CA_Hide);
    mproject->addAction(command, ProjectExplorer::Constants::G_PROJECT_BUILD);
    msubproject->addAction(command, ProjectExplorer::Constants::G_PROJECT_BUILD);
    connect(m_runCMakeActionContextMenu, SIGNAL(triggered()), this, SLOT(runCMakeContextMenu()));

}

void CMakeManager::updateContextMenu(ProjectExplorer::Project *project, ProjectExplorer::Node *node)
{
    Q_UNUSED(node);
    m_contextProject = project;
}

void CMakeManager::runCMake()
{
    runCMake(ProjectExplorer::ProjectExplorerPlugin::instance()->currentProject());
}

void CMakeManager::runCMakeContextMenu()
{
    runCMake(m_contextProject);
}

void CMakeManager::runCMake(ProjectExplorer::Project *project)
{
    if (!project)
        return;
    CMakeProject *cmakeProject = qobject_cast<CMakeProject *>(project);
    if (!cmakeProject)
        return;

    if (!cmakeProject->activeTarget())
        return;
    if (!cmakeProject->activeTarget()->activeBuildConfiguration())
        return;
    CMakeBuildConfiguration *bc = cmakeProject->activeTarget()->activeBuildConfiguration();
    CMakeOpenProjectWizard copw(this,
                                cmakeProject->projectDirectory(),
                                bc->buildDirectory(),
                                CMakeOpenProjectWizard::WantToUpdate,
                                bc->environment());
    if (copw.exec() == QDialog::Accepted) {
        cmakeProject->parseCMakeLists();
    }
133
134
}

135
ProjectExplorer::Project *CMakeManager::openProject(const QString &fileName, QString *errorString)
con's avatar
con committed
136
{
137
    Q_UNUSED(errorString)
Tobias Hunger's avatar
Tobias Hunger committed
138
    // TODO check whether this project is already opened
con's avatar
con committed
139
140
141
142
143
144
145
    return new CMakeProject(this, fileName);
}

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

dt's avatar
dt committed
147
148
149
150
151
QString CMakeManager::cmakeExecutable() const
{
    return m_settingsPage->cmakeExecutable();
}

152
153
154
155
156
157
158
159
160
161
bool CMakeManager::isCMakeExecutableValid() const
{
    return m_settingsPage->isCMakeExecutableValid();
}

void CMakeManager::setCMakeExecutable(const QString &executable)
{
    m_settingsPage->setCMakeExecutable(executable);
}

162
163
164
165
166
bool CMakeManager::hasCodeBlocksMsvcGenerator() const
{
    return m_settingsPage->hasCodeBlocksMsvcGenerator();
}

dt's avatar
dt committed
167
168
169
170
// TODO need to refactor this out
// we probably want the process instead of this function
// cmakeproject then could even run the cmake process in the background, adding the files afterwards
// sounds like a plan
171
void CMakeManager::createXmlFile(Utils::QtcProcess *proc, const QString &arguments,
172
173
                                 const QString &sourceDirectory, const QDir &buildDirectory,
                                 const Utils::Environment &env, const QString &generator)
dt's avatar
dt committed
174
175
176
177
178
179
180
181
182
183
184
{
    // We create a cbp file, only if we didn't find a cbp file in the base directory
    // Yet that can still override cbp files in subdirectories
    // And we are creating tons of files in the source directories
    // All of that is not really nice.
    // The mid term plan is to move away from the CodeBlocks Generator and use our own
    // QtCreator generator, which actually can be very similar to the CodeBlock Generator

    // TODO we need to pass on the same paremeters as the cmakestep
    QString buildDirectoryPath = buildDirectory.absolutePath();
    buildDirectory.mkpath(buildDirectoryPath);
185
    proc->setWorkingDirectory(buildDirectoryPath);
186
    proc->setEnvironment(env);
187

188
189
    const QString srcdir = buildDirectory.exists(QLatin1String("CMakeCache.txt")) ?
                QString(QLatin1Char('.')) : sourceDirectory;
190
191
192
193
194
195
    QString args;
    Utils::QtcProcess::addArg(&args, srcdir);
    Utils::QtcProcess::addArgs(&args, arguments);
    Utils::QtcProcess::addArg(&args, generator);
    proc->setCommand(cmakeExecutable(), args);
    proc->start();
dt's avatar
dt committed
196
197
}

198
199
200
201
202
203
QString CMakeManager::findCbpFile(const QDir &directory)
{
    // Find the cbp file
    //   TODO the cbp file is named like the project() command in the CMakeList.txt file
    //   so this method below could find the wrong cbp file, if the user changes the project()
    //   2name
204
205
    QDateTime t;
    QString file;
206
    foreach (const QString &cbpFile , directory.entryList()) {
207
208
209
210
211
212
213
        if (cbpFile.endsWith(QLatin1String(".cbp"))) {
            QFileInfo fi(directory.path() + QLatin1Char('/') + cbpFile);
            if (t.isNull() || fi.lastModified() > t) {
                file = directory.path() + QLatin1Char('/') + cbpFile;
                t = fi.lastModified();
            }
        }
214
    }
215
    return file;
216
217
}

218
219
220
221
// This code is duplicated from qtversionmanager
QString CMakeManager::qtVersionForQMake(const QString &qmakePath)
{
    QProcess qmake;
222
    qmake.start(qmakePath, QStringList(QLatin1String("--version")));
223
224
225
226
227
228
229
    if (!qmake.waitForStarted()) {
        qWarning("Cannot start '%s': %s", qPrintable(qmakePath), qPrintable(qmake.errorString()));
        return QString();
    }
    if (!qmake.waitForFinished())      {
        Utils::SynchronousProcess::stopProcess(qmake);
        qWarning("Timeout running '%s'.", qPrintable(qmakePath));
230
        return QString();
231
    }
232
    QString output = qmake.readAllStandardOutput();
233
    QRegExp regexp(QLatin1String("(QMake version|Qmake version:)[\\s]*([\\d.]*)"));
234
    regexp.indexIn(output);
235
236
    if (regexp.cap(2).startsWith(QLatin1String("2."))) {
        QRegExp regexp2(QLatin1String("Using Qt version[\\s]*([\\d\\.]*)"));
237
238
239
240
241
        regexp2.indexIn(output);
        return regexp2.cap(1);
    }
    return QString();
}
242

243
244
/////
// CMakeSettingsPage
245
////
246

247
248

CMakeSettingsPage::CMakeSettingsPage()
dt's avatar
dt committed
249
    :  m_pathchooser(0)
250
{
dt's avatar
dt committed
251
252
    m_userCmake.process = 0;
    m_pathCmake.process = 0;
253
254
    m_userCmake.hasCodeBlocksMsvcGenerator = false;
    m_pathCmake.hasCodeBlocksMsvcGenerator = false;
255
256
257
    Core::ICore *core = Core::ICore::instance();
    QSettings * settings = core->settings();
    settings->beginGroup(QLatin1String("CMakeSettings"));
dt's avatar
dt committed
258
    m_userCmake.executable = settings->value(QLatin1String("cmakeExecutable")).toString();
259
    settings->endGroup();
dt's avatar
dt committed
260
261
262
263

    updateInfo(&m_userCmake);
    m_pathCmake.executable = findCmakeExecutable();
    updateInfo(&m_pathCmake);
264
265
}

dt's avatar
dt committed
266
void CMakeSettingsPage::startProcess(CMakeValidator *cmakeValidator)
267
{
dt's avatar
dt committed
268
269
270
271
272
273
274
275
    cmakeValidator->process = new QProcess();

    if (cmakeValidator == &m_pathCmake) // ugly
        connect(cmakeValidator->process, SIGNAL(finished(int)),
                this, SLOT(userCmakeFinished()));
    else
        connect(cmakeValidator->process, SIGNAL(finished(int)),
                this, SLOT(pathCmakeFinished()));
dt's avatar
dt committed
276

dt's avatar
dt committed
277
278
279
    cmakeValidator->process->start(cmakeValidator->executable, QStringList(QLatin1String("--help")));
    cmakeValidator->process->waitForStarted();
}
dt's avatar
dt committed
280

dt's avatar
dt committed
281
282
283
void CMakeSettingsPage::userCmakeFinished()
{
    cmakeFinished(&m_userCmake);
dt's avatar
dt committed
284
285
}

dt's avatar
dt committed
286
void CMakeSettingsPage::pathCmakeFinished()
dt's avatar
dt committed
287
{
dt's avatar
dt committed
288
289
290
291
292
293
    cmakeFinished(&m_pathCmake);
}

void CMakeSettingsPage::cmakeFinished(CMakeValidator *cmakeValidator) const
{
    if (cmakeValidator->process) {
dt's avatar
dt committed
294
        cmakeValidator->process->waitForFinished();
dt's avatar
dt committed
295
        QString response = cmakeValidator->process->readAll();
296
297
        QRegExp versionRegexp(QLatin1String("^cmake version ([\\d\\.]*)"));
        versionRegexp.indexIn(response);
dt's avatar
dt committed
298

299
        //m_supportsQtCreator = response.contains(QLatin1String("QtCreator"));
dt's avatar
dt committed
300
301
        cmakeValidator->hasCodeBlocksMsvcGenerator = response.contains(QLatin1String("CodeBlocks - NMake Makefiles"));
        cmakeValidator->version = versionRegexp.cap(1);
302
        if (!(versionRegexp.capturedTexts().size() > 3))
dt's avatar
dt committed
303
            cmakeValidator->version += QLatin1Char('.') + versionRegexp.cap(3);
dt's avatar
dt committed
304

dt's avatar
dt committed
305
306
        if (cmakeValidator->version.isEmpty())
            cmakeValidator->state = CMakeValidator::INVALID;
307
        else
dt's avatar
dt committed
308
            cmakeValidator->state = CMakeValidator::VALID;
309

dt's avatar
dt committed
310
311
        cmakeValidator->process->deleteLater();
        cmakeValidator->process = 0;
312
    }
313
314
}

dt's avatar
dt committed
315
bool CMakeSettingsPage::isCMakeExecutableValid() const
dt's avatar
dt committed
316
{
dt's avatar
dt committed
317
318
    if (m_userCmake.state == CMakeValidator::RUNNING) {
        disconnect(m_userCmake.process, SIGNAL(finished(int)),
dt's avatar
dt committed
319
                   this, SLOT(userCmakeFinished()));
dt's avatar
dt committed
320
321
322
323
324
325
326
327
328
        m_userCmake.process->waitForFinished();
        // Parse the output now
        cmakeFinished(&m_userCmake);
    }

    if (m_userCmake.state == CMakeValidator::VALID)
        return true;
    if (m_pathCmake.state == CMakeValidator::RUNNING) {
        disconnect(m_userCmake.process, SIGNAL(finished(int)),
dt's avatar
dt committed
329
                   this, SLOT(pathCmakeFinished()));
dt's avatar
dt committed
330
        m_pathCmake.process->waitForFinished();
331
        // Parse the output now
dt's avatar
dt committed
332
        cmakeFinished(&m_pathCmake);
333
    }
dt's avatar
dt committed
334
    return m_pathCmake.state == CMakeValidator::VALID;
dt's avatar
dt committed
335
336
}

337
CMakeSettingsPage::~CMakeSettingsPage()
dt's avatar
dt committed
338
{
dt's avatar
dt committed
339
340
341
342
343
344
    if (m_userCmake.process)
        m_userCmake.process->waitForFinished();
    delete m_userCmake.process;
    if (m_pathCmake.process)
        m_pathCmake.process->waitForFinished();
    delete m_pathCmake.process;
345
346
347
348
}

QString CMakeSettingsPage::findCmakeExecutable() const
{
349
    Utils::Environment env = Utils::Environment::systemEnvironment();
350
    return env.searchInPath(QLatin1String("cmake"));
351
352
}

353
354
QString CMakeSettingsPage::id() const
{
355
    return QLatin1String("Z.CMake");
356
}
357

358
QString CMakeSettingsPage::displayName() const
359
{
360
    return tr("CMake");
361
362
363
364
}

QString CMakeSettingsPage::category() const
{
365
    return QLatin1String(ProjectExplorer::Constants::PROJECTEXPLORER_SETTINGS_CATEGORY);
366
367
}

368
QString CMakeSettingsPage::displayCategory() const
369
{
370
371
    return QCoreApplication::translate("ProjectExplorer",
                                       ProjectExplorer::Constants::PROJECTEXPLORER_SETTINGS_TR_CATEGORY);
372
373
}

374
375
QIcon CMakeSettingsPage::categoryIcon() const
{
376
    return QIcon(QLatin1String(ProjectExplorer::Constants::PROJECTEXPLORER_SETTINGS_CATEGORY_ICON));
377
378
}

379
380
QWidget *CMakeSettingsPage::createPage(QWidget *parent)
{
381
    QWidget *outerWidget = new QWidget(parent);
382
    QFormLayout *formLayout = new QFormLayout(outerWidget);
383
    formLayout->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow);
384
    m_pathchooser = new Utils::PathChooser;
385
    m_pathchooser->setExpectedKind(Utils::PathChooser::ExistingCommand);
386
    formLayout->addRow(tr("Executable:"), m_pathchooser);
387
    formLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Ignored, QSizePolicy::MinimumExpanding));
dt's avatar
dt committed
388
    m_pathchooser->setPath(m_userCmake.executable);
389
    return outerWidget;
390
391
}

dt's avatar
dt committed
392
void CMakeSettingsPage::updateInfo(CMakeValidator *cmakeValidator)
393
{
dt's avatar
dt committed
394
    QFileInfo fi(cmakeValidator->executable);
395
396
    if (fi.exists() && fi.isExecutable()) {
        // Run it to find out more
dt's avatar
dt committed
397
398
        cmakeValidator->state = CMakeValidator::RUNNING;
        startProcess(cmakeValidator);
399
    } else {
dt's avatar
dt committed
400
        cmakeValidator->state = CMakeValidator::INVALID;
401
402
403
404
    }
    saveSettings();
}

405
406
407
void CMakeSettingsPage::saveSettings() const
{
    QSettings *settings = Core::ICore::instance()->settings();
408
    settings->beginGroup(QLatin1String("CMakeSettings"));
dt's avatar
dt committed
409
    settings->setValue(QLatin1String("cmakeExecutable"), m_userCmake.executable);
410
411
412
413
414
    settings->endGroup();
}

void CMakeSettingsPage::apply()
{
415
416
    if (!m_pathchooser) // page was never shown
        return;
dt's avatar
dt committed
417
    if (m_userCmake.executable == m_pathchooser->path())
418
        return;
dt's avatar
dt committed
419
420
    m_userCmake.executable = m_pathchooser->path();
    updateInfo(&m_userCmake);
421
422
423
424
425
426
427
428
429
}

void CMakeSettingsPage::finish()
{

}

QString CMakeSettingsPage::cmakeExecutable() const
{
dt's avatar
dt committed
430
431
432
433
434
435
    if (!isCMakeExecutableValid())
        return QString();
    if (m_userCmake.state == CMakeValidator::VALID)
        return m_userCmake.executable;
    else
        return m_pathCmake.executable;
436
437
}

438
void CMakeSettingsPage::setCMakeExecutable(const QString &executable)
439
{
dt's avatar
dt committed
440
    if (m_userCmake.executable == executable)
441
        return;
dt's avatar
dt committed
442
443
    m_userCmake.executable = executable;
    updateInfo(&m_userCmake);
444
445
}

446
bool CMakeSettingsPage::hasCodeBlocksMsvcGenerator() const
447
{
dt's avatar
dt committed
448
449
450
451
452
453
    if (!isCMakeExecutableValid())
        return false;
    if (m_userCmake.state == CMakeValidator::VALID)
        return m_userCmake.hasCodeBlocksMsvcGenerator;
    else
        return m_pathCmake.hasCodeBlocksMsvcGenerator;
454
}