buildmanager.cpp 17 KB
Newer Older
1
/**************************************************************************
con's avatar
con committed
2
3
4
**
** This file is part of Qt Creator
**
hjk's avatar
hjk committed
5
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
con's avatar
con committed
6
**
7
** Contact: Nokia Corporation (qt-info@nokia.com)
con's avatar
con committed
8
**
9
** Commercial Usage
10
**
11
12
13
14
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
15
**
16
** GNU Lesser General Public License Usage
17
**
18
19
20
21
22
23
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24
**
25
** If you are unsure which license is appropriate for your use, please
hjk's avatar
hjk committed
26
** contact the sales department at http://qt.nokia.com/contact.
con's avatar
con committed
27
**
28
**************************************************************************/
hjk's avatar
hjk committed
29

con's avatar
con committed
30
#include "buildmanager.h"
hjk's avatar
hjk committed
31
32

#include "buildprogress.h"
con's avatar
con committed
33
34
35
#include "buildstep.h"
#include "compileoutputwindow.h"
#include "projectexplorerconstants.h"
hjk's avatar
hjk committed
36
#include "projectexplorer.h"
37
#include "project.h"
38
#include "projectexplorersettings.h"
Tobias Hunger's avatar
Tobias Hunger committed
39
#include "target.h"
hjk's avatar
hjk committed
40
#include "taskwindow.h"
41
#include "buildconfiguration.h"
con's avatar
con committed
42

43
#include <coreplugin/icore.h>
44
#include <coreplugin/progressmanager/progressmanager.h>
con's avatar
con committed
45
#include <coreplugin/progressmanager/futureprogress.h>
46
#include <projectexplorer/session.h>
hjk's avatar
hjk committed
47
48
#include <extensionsystem/pluginmanager.h>
#include <utils/qtcassert.h>
con's avatar
con committed
49
50
51

#include <QtCore/QDir>
#include <QtCore/QTimer>
52
53
54

#include <qtconcurrent/QtConcurrentTools>

con's avatar
con committed
55
56
57
#include <QtGui/QHeaderView>
#include <QtGui/QIcon>
#include <QtGui/QLabel>
58
59
#include <QtGui/QApplication>
#include <QtGui/QMainWindow>
con's avatar
con committed
60
61
62
63

using namespace ProjectExplorer;
using namespace ProjectExplorer::Internal;

Friedemann Kleint's avatar
Friedemann Kleint committed
64
65
static inline QString msgProgress(int n, int total)
{
Friedemann Kleint's avatar
Friedemann Kleint committed
66
    return BuildManager::tr("Finished %1 of %n build steps", 0, n).arg(total);
Friedemann Kleint's avatar
Friedemann Kleint committed
67
68
}

con's avatar
con committed
69
70
71
72
73
74
75
76
77
78
79
80
81
82
BuildManager::BuildManager(ProjectExplorerPlugin *parent)
    : QObject(parent)
    , m_running(false)
    , m_previousBuildStepProject(0)
    , m_canceling(false)
    , m_maxProgress(0)
    , m_progressFutureInterface(0)
{
    ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
    m_projectExplorerPlugin = parent;

    connect(&m_watcher, SIGNAL(finished()),
            this, SLOT(nextBuildQueue()));

83
84
85
86
87
    connect(&m_watcher, SIGNAL(progressValueChanged(int)),
            this, SLOT(progressChanged()));
    connect(&m_watcher, SIGNAL(progressRangeChanged(int, int)),
            this, SLOT(progressChanged()));

88
89
90
    connect(parent->session(), SIGNAL(aboutToRemoveProject(ProjectExplorer::Project *)),
            this, SLOT(aboutToRemoveProject(ProjectExplorer::Project *)));

con's avatar
con committed
91
92
93
94
95
96
    m_outputWindow = new CompileOutputWindow(this);
    pm->addObject(m_outputWindow);

    m_taskWindow = new TaskWindow;
    pm->addObject(m_taskWindow);

97
    m_taskWindow->addCategory(Constants::TASK_CATEGORY_COMPILE, tr("Compile", "Category for compiler isses listened under 'Build Issues'"));
98
    m_taskWindow->addCategory(Constants::TASK_CATEGORY_BUILDSYSTEM, tr("Build System", "Category for build system isses listened under 'Build Issues'"));
99

con's avatar
con committed
100
    connect(m_taskWindow, SIGNAL(tasksChanged()),
101
            this, SLOT(updateTaskCount()));
con's avatar
con committed
102
103
104

    connect(&m_progressWatcher, SIGNAL(canceled()),
            this, SLOT(cancel()));
105
106
    connect(&m_progressWatcher, SIGNAL(finished()),
            this, SLOT(finish()));
con's avatar
con committed
107
108
109
110
111
112
113
114
115
116
117
118
119
120
}

BuildManager::~BuildManager()
{
    cancel();
    ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();

    pm->removeObject(m_taskWindow);
    delete m_taskWindow;

    pm->removeObject(m_outputWindow);
    delete m_outputWindow;
}

121
122
123
124
125
126
127
128
129
130
131
132
void BuildManager::aboutToRemoveProject(ProjectExplorer::Project *p)
{
    QHash<Project *, int>::iterator it = m_activeBuildSteps.find(p);
    QHash<Project *, int>::iterator end = m_activeBuildSteps.end();
    if (it != end && *it > 0) {
        // We are building the project that's about to be removed.
        // We cancel the whole queue, which isn't the nicest thing to do
        // but a safe thing.
        cancel();
    }
}

con's avatar
con committed
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
bool BuildManager::isBuilding() const
{
    // we are building even if we are not running yet
    return !m_buildQueue.isEmpty() || m_running;
}

void BuildManager::cancel()
{
    if (m_running) {
        m_canceling = true;
        m_watcher.cancel();
        m_watcher.waitForFinished();

        // The cancel message is added to the output window via a single shot timer
        // since the canceling is likely to have generated new addToOutputWindow signals
        // which are waiting in the event queue to be processed
        // (And we want those to be before the cancel message.)
        QTimer::singleShot(0, this, SLOT(emitCancelMessage()));

152
153
        disconnect(m_currentBuildStep, SIGNAL(addTask(ProjectExplorer::Task)),
                   this, SLOT(addToTaskWindow(ProjectExplorer::Task)));
154
155
        disconnect(m_currentBuildStep, SIGNAL(addOutput(QString, QTextCharFormat)),
                   this, SLOT(addToOutputWindow(QString, QTextCharFormat)));
Tobias Hunger's avatar
Tobias Hunger committed
156
        decrementActiveBuildSteps(m_currentBuildStep->buildConfiguration()->target()->project());
con's avatar
con committed
157

158
        m_progressFutureInterface->setProgressValueAndText(m_progress*100, "Build canceled"); //TODO NBS fix in qtconcurrent
con's avatar
con committed
159
160
161
162
163
        clearBuildQueue();
    }
    return;
}

164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
void BuildManager::updateTaskCount()
{
    Core::ProgressManager *progressManager = Core::ICore::instance()->progressManager();
    int errors = m_taskWindow->errorTaskCount();
    if (errors > 0) {
        progressManager->setApplicationLabel(QString("%1").arg(errors));
    } else {
        progressManager->setApplicationLabel("");
    }
    emit tasksChanged();
}

void BuildManager::finish()
{
    QApplication::alert(Core::ICore::instance()->mainWindow(), 3000);
}

con's avatar
con committed
181
182
void BuildManager::emitCancelMessage()
{
183
184
185
    QTextCharFormat textCharFormat;
    textCharFormat.setForeground(Qt::red);
    emit addToOutputWindow(tr("Canceled build."), textCharFormat);
con's avatar
con committed
186
187
188
189
}

void BuildManager::clearBuildQueue()
{
190
    foreach (BuildStep *bs, m_buildQueue) {
Tobias Hunger's avatar
Tobias Hunger committed
191
        decrementActiveBuildSteps(bs->buildConfiguration()->target()->project());
192
193
        disconnect(bs, SIGNAL(addTask(ProjectExplorer::Task)),
                   this, SLOT(addToTaskWindow(ProjectExplorer::Task)));
194
195
        disconnect(bs, SIGNAL(addOutput(QString, QTextCharFormat)),
                   this, SLOT(addToOutputWindow(QString, QTextCharFormat)));
196
    }
con's avatar
con committed
197
198
199
200

    m_buildQueue.clear();
    m_running = false;
    m_previousBuildStepProject = 0;
201
    m_currentBuildStep = 0;
con's avatar
con committed
202
203
204

    m_progressFutureInterface->reportCanceled();
    m_progressFutureInterface->reportFinished();
205
    m_progressWatcher.setFuture(QFuture<void>());
con's avatar
con committed
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
    delete m_progressFutureInterface;
    m_progressFutureInterface = 0;
    m_maxProgress = 0;

    emit buildQueueFinished(false);
}


void BuildManager::toggleOutputWindow()
{
    m_outputWindow->toggle(false);
}

void BuildManager::showTaskWindow()
{
    m_taskWindow->popup(false);
}

void BuildManager::toggleTaskWindow()
{
    m_taskWindow->toggle(false);
}

bool BuildManager::tasksAvailable() const
{
231
    return m_taskWindow->taskCount() > 0;
con's avatar
con committed
232
233
234
235
236
237
238
239
240
}

void BuildManager::gotoTaskWindow()
{
    m_taskWindow->popup(true);
}

void BuildManager::startBuildQueue()
{
241
242
    if (m_buildQueue.isEmpty()) {
        emit buildQueueFinished(true);
243
        return;
244
    }
con's avatar
con committed
245
246
    if (!m_running) {
        // Progress Reporting
247
        Core::ProgressManager *progressManager = Core::ICore::instance()->progressManager();
con's avatar
con committed
248
249
        m_progressFutureInterface = new QFutureInterface<void>;
        m_progressWatcher.setFuture(m_progressFutureInterface->future());
250
251
252
        m_outputWindow->clearContents();
        m_taskWindow->clearTasks(Constants::TASK_CATEGORY_COMPILE);
        m_taskWindow->clearTasks(Constants::TASK_CATEGORY_BUILDSYSTEM);
253
        progressManager->setApplicationLabel("");
con's avatar
con committed
254
        Core::FutureProgress *progress = progressManager->addTask(m_progressFutureInterface->future(),
255
256
257
              tr("Build"),
              Constants::TASK_BUILD,
              Core::ProgressManager::KeepOnFinish | Core::ProgressManager::ShowInApplicationIcon);
con's avatar
con committed
258
259
260
        connect(progress, SIGNAL(clicked()), this, SLOT(showBuildResults()));
        progress->setWidget(new BuildProgress(m_taskWindow));
        m_progress = 0;
261
        m_progressFutureInterface->setProgressRange(0, m_maxProgress * 100);
con's avatar
con committed
262
263
264
265
266
267
268

        m_running = true;
        m_canceling = false;
        m_progressFutureInterface->reportStarted();
        nextStep();
    } else {
        // Already running
269
        m_progressFutureInterface->setProgressRange(0, m_maxProgress * 100);
Friedemann Kleint's avatar
Friedemann Kleint committed
270
        m_progressFutureInterface->setProgressValueAndText(m_progress*100, msgProgress(m_progress, m_maxProgress));
con's avatar
con committed
271
272
273
274
275
    }
}

void BuildManager::showBuildResults()
{
276
    if (m_taskWindow->taskCount() != 0)
con's avatar
con committed
277
278
279
280
281
282
        toggleTaskWindow();
    else
        toggleOutputWindow();
    //toggleTaskWindow();
}

283
void BuildManager::addToTaskWindow(const ProjectExplorer::Task &task)
con's avatar
con committed
284
{
285
    m_taskWindow->addTask(task);
con's avatar
con committed
286
287
}

288
void BuildManager::addToOutputWindow(const QString &string, const QTextCharFormat &format)
con's avatar
con committed
289
{
290
    m_outputWindow->appendText(string, format);
con's avatar
con committed
291
292
293
294
295
296
297
}

void BuildManager::nextBuildQueue()
{
    if (m_canceling)
        return;

298
299
    disconnect(m_currentBuildStep, SIGNAL(addTask(ProjectExplorer::Task)),
               this, SLOT(addToTaskWindow(ProjectExplorer::Task)));
300
301
    disconnect(m_currentBuildStep, SIGNAL(addOutput(QString, QTextCharFormat)),
               this, SLOT(addToOutputWindow(QString, QTextCharFormat)));
con's avatar
con committed
302
303

    ++m_progress;
Friedemann Kleint's avatar
Friedemann Kleint committed
304
    m_progressFutureInterface->setProgressValueAndText(m_progress*100, msgProgress(m_progress, m_maxProgress));
Tobias Hunger's avatar
Tobias Hunger committed
305
    decrementActiveBuildSteps(m_currentBuildStep->buildConfiguration()->target()->project());
con's avatar
con committed
306
307
308
309

    bool result = m_watcher.result();
    if (!result) {
        // Build Failure
310
311
        const QString projectName = m_currentBuildStep->buildConfiguration()->target()->project()->displayName();
        const QString targetName = m_currentBuildStep->buildConfiguration()->target()->displayName();
312
313
314
315
        QTextCharFormat textCharFormat;
        textCharFormat.setForeground(Qt::red);
        addToOutputWindow(tr("Error while building project %1 (target: %2)").arg(projectName, targetName), textCharFormat);
        addToOutputWindow(tr("When executing build step '%1'").arg(m_currentBuildStep->displayName()), textCharFormat);
con's avatar
con committed
316
        // NBS TODO fix in qtconcurrent
317
        m_progressFutureInterface->setProgressValueAndText(m_progress*100, tr("Error while building project %1 (target: %2)").arg(projectName, targetName));
con's avatar
con committed
318
319
320
321
322
323
324
325
    }

    if (result)
        nextStep();
    else
        clearBuildQueue();
}

326
327
328
329
330
331
332
333
334
335
336
void BuildManager::progressChanged()
{
    if (!m_progressFutureInterface)
        return;
    int range = m_watcher.progressMaximum() - m_watcher.progressMinimum();
    if (range != 0) {
        int percent = (m_watcher.progressValue() - m_watcher.progressMinimum()) * 100 / range;
        m_progressFutureInterface->setProgressValue(m_progress * 100 + percent);
    }
}

con's avatar
con committed
337
338
339
340
341
342
void BuildManager::nextStep()
{
    if (!m_buildQueue.empty()) {
        m_currentBuildStep = m_buildQueue.front();
        m_buildQueue.pop_front();

Tobias Hunger's avatar
Tobias Hunger committed
343
        if (m_currentBuildStep->buildConfiguration()->target()->project() != m_previousBuildStepProject) {
dt's avatar
dt committed
344
            const QString projectName = m_currentBuildStep->buildConfiguration()->target()->project()->displayName();
345
346
            QTextCharFormat textCharFormat;
            textCharFormat.setFontWeight(QFont::Bold);
dt's avatar
dt committed
347
            addToOutputWindow(tr("Running build steps for project %1...")
348
                              .arg(projectName), textCharFormat);
Tobias Hunger's avatar
Tobias Hunger committed
349
            m_previousBuildStepProject = m_currentBuildStep->buildConfiguration()->target()->project();
con's avatar
con committed
350
351
352
353
        }
        m_watcher.setFuture(QtConcurrent::run(&BuildStep::run, m_currentBuildStep));
    } else {
        m_running = false;
354
        m_previousBuildStepProject = 0;
con's avatar
con committed
355
        m_progressFutureInterface->reportFinished();
356
        m_progressWatcher.setFuture(QFuture<void>());
357
        m_currentBuildStep = 0;
con's avatar
con committed
358
359
360
361
362
363
364
        delete m_progressFutureInterface;
        m_progressFutureInterface = 0;
        m_maxProgress = 0;
        emit buildQueueFinished(true);
    }
}

dt's avatar
dt committed
365
bool BuildManager::buildQueueAppend(QList<BuildStep *> steps)
con's avatar
con committed
366
{
dt's avatar
dt committed
367
368
369
370
371
    int count = steps.size();
    bool init = true;
    int i = 0;
    for (; i < count; ++i) {
        BuildStep *bs = steps.at(i);
372
373
        connect(bs, SIGNAL(addTask(ProjectExplorer::Task)),
                this, SLOT(addToTaskWindow(ProjectExplorer::Task)));
374
375
        connect(bs, SIGNAL(addOutput(QString, QTextCharFormat)),
                this, SLOT(addToOutputWindow(QString, QTextCharFormat)));
dt's avatar
dt committed
376
377
378
379
        init = bs->init();
        if (!init)
            break;
    }
380
    if (!init) {
dt's avatar
dt committed
381
382
383
384
385
386
        BuildStep *bs = steps.at(i);

        // cleaning up
        // print something for the user
        const QString projectName = bs->buildConfiguration()->target()->project()->displayName();
        const QString targetName = bs->buildConfiguration()->target()->displayName();
387
388
389
390
        QTextCharFormat textCharFormat;
        textCharFormat.setForeground(Qt::red);
        addToOutputWindow(tr("Error while building project %1 (target: %2)").arg(projectName, targetName), textCharFormat);
        addToOutputWindow(tr("When executing build step '%1'").arg(bs->displayName()), textCharFormat);
dt's avatar
dt committed
391
392
393
394

        // disconnect the buildsteps again
        for (int j = 0; j <= i; ++j) {
            BuildStep *bs = steps.at(j);
395
396
            disconnect(bs, SIGNAL(addTask(ProjectExplorer::Task)),
                       this, SLOT(addToTaskWindow(ProjectExplorer::Task)));
397
398
            disconnect(bs, SIGNAL(addOutput(QString, QTextCharFormat)),
                       this, SLOT(addToOutputWindow(QString, QTextCharFormat)));
dt's avatar
dt committed
399
        }
400
401
402
        return false;
    }

dt's avatar
dt committed
403
404
405
406
407
408
    // Everthing init() well
    for (i = 0; i < count; ++i) {
        ++m_maxProgress;
        m_buildQueue.append(steps.at(i));
        incrementActiveBuildSteps(steps.at(i)->buildConfiguration()->target()->project());
    }
409
    return true;
con's avatar
con committed
410
411
}

dt's avatar
dt committed
412
void BuildManager::buildProjects(const QList<BuildConfiguration *> &configurations)
con's avatar
con committed
413
{
dt's avatar
dt committed
414
415
    QList<BuildStep *> steps;
    foreach(BuildConfiguration *bc, configurations)
416
        steps.append(bc->steps(Build));
dt's avatar
dt committed
417
418
419
420
421

    bool success = buildQueueAppend(steps);
    if (!success) {
        m_outputWindow->popup(false);
        return;
con's avatar
con committed
422
    }
dt's avatar
dt committed
423

424
425
    if (ProjectExplorerPlugin::instance()->projectExplorerSettings().showCompilerOutput)
        m_outputWindow->popup(false);
426
    startBuildQueue();
con's avatar
con committed
427
428
}

dt's avatar
dt committed
429
void BuildManager::cleanProjects(const QList<BuildConfiguration *> &configurations)
con's avatar
con committed
430
{
dt's avatar
dt committed
431
432
    QList<BuildStep *> steps;
    foreach(BuildConfiguration *bc, configurations)
433
        steps.append(bc->steps(Clean));
dt's avatar
dt committed
434
435
436
437
438

    bool success = buildQueueAppend(steps);
    if (!success) {
        m_outputWindow->popup(false);
        return;
con's avatar
con committed
439
    }
dt's avatar
dt committed
440

441
442
    if (ProjectExplorerPlugin::instance()->projectExplorerSettings().showCompilerOutput)
        m_outputWindow->popup(false);
443
    startBuildQueue();
con's avatar
con committed
444
445
}

dt's avatar
dt committed
446
void BuildManager::buildProject(BuildConfiguration *configuration)
con's avatar
con committed
447
{
dt's avatar
dt committed
448
    buildProjects(QList<BuildConfiguration *>() << configuration);
con's avatar
con committed
449
450
}

dt's avatar
dt committed
451
void BuildManager::cleanProject(BuildConfiguration *configuration)
con's avatar
con committed
452
{
dt's avatar
dt committed
453
    cleanProjects(QList<BuildConfiguration *>() << configuration);
con's avatar
con committed
454
455
}

456
void BuildManager::appendStep(BuildStep *step)
con's avatar
con committed
457
{
dt's avatar
dt committed
458
459
460
461
462
463
464
    bool success = buildQueueAppend(QList<BuildStep *>() << step);
    if (!success) {
        m_outputWindow->popup(false);
        return;
    }
    if (ProjectExplorerPlugin::instance()->projectExplorerSettings().showCompilerOutput)
        m_outputWindow->popup(false);
con's avatar
con committed
465
466
467
468
469
470
471
472
473
474
475
476
477
    startBuildQueue();
}

bool BuildManager::isBuilding(Project *pro)
{
    QHash<Project *, int>::iterator it = m_activeBuildSteps.find(pro);
    QHash<Project *, int>::iterator end = m_activeBuildSteps.end();
    if (it == end || *it == 0)
        return false;
    else
        return true;
}

478
479
480
481
482
bool BuildManager::isBuilding(BuildStep *step)
{
    return (m_currentBuildStep == step) || m_buildQueue.contains(step);
}

con's avatar
con committed
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
void BuildManager::incrementActiveBuildSteps(Project *pro)
{
    QHash<Project *, int>::iterator it = m_activeBuildSteps.find(pro);
    QHash<Project *, int>::iterator end = m_activeBuildSteps.end();
    if (it == end) {
        m_activeBuildSteps.insert(pro, 1);
        emit buildStateChanged(pro);
    } else if (*it == 0) {
        ++*it;
        emit buildStateChanged(pro);
    } else {
        ++*it;
    }
}

void BuildManager::decrementActiveBuildSteps(Project *pro)
{
    QHash<Project *, int>::iterator it = m_activeBuildSteps.find(pro);
    QHash<Project *, int>::iterator end = m_activeBuildSteps.end();
    if (it == end) {
dt's avatar
dt committed
503
        Q_ASSERT(false && "BuildManager m_activeBuildSteps says project is not building, but apparently a build step was still in the queue.");
con's avatar
con committed
504
505
506
507
508
509
510
    } else if (*it == 1) {
        --*it;
        emit buildStateChanged(pro);
    } else {
        --*it;
    }
}