buildmanager.cpp 22.7 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) 2012 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

con's avatar
con committed
33
#include "buildmanager.h"
hjk's avatar
hjk committed
34 35

#include "buildprogress.h"
Tobias Hunger's avatar
Tobias Hunger committed
36
#include "buildsteplist.h"
con's avatar
con committed
37 38
#include "compileoutputwindow.h"
#include "projectexplorerconstants.h"
hjk's avatar
hjk committed
39
#include "projectexplorer.h"
40
#include "project.h"
41
#include "projectexplorersettings.h"
Tobias Hunger's avatar
Tobias Hunger committed
42
#include "target.h"
hjk's avatar
hjk committed
43
#include "taskwindow.h"
dt's avatar
dt committed
44
#include "taskhub.h"
con's avatar
con committed
45

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

#include <QtCore/QDir>
54
#include <QtCore/QTime>
con's avatar
con committed
55
#include <QtCore/QTimer>
56
#include <QtCore/QMetaType>
57 58 59
#include <QtCore/QList>
#include <QtCore/QHash>
#include <QtCore/QFutureWatcher>
60 61 62

#include <qtconcurrent/QtConcurrentTools>

63 64
#include <QtGui/QApplication>
#include <QtGui/QMainWindow>
con's avatar
con committed
65

66
static inline QString msgProgress(int progress, int total)
Friedemann Kleint's avatar
Friedemann Kleint committed
67
{
68
    return ProjectExplorer::BuildManager::tr("Finished %1 of %n steps", 0, total).arg(progress);
69 70 71 72 73 74 75 76 77 78 79 80 81 82
}

namespace ProjectExplorer {
//NBS TODO this class has too many different variables which hold state:
// m_buildQueue, m_running, m_canceled, m_progress, m_maxProgress, m_activeBuildSteps and ...
// I might need to reduce that.
struct BuildManagerPrivate {
    BuildManagerPrivate();

    Internal::CompileOutputWindow *m_outputWindow;
    TaskHub *m_taskHub;
    Internal::TaskWindow *m_taskWindow;

    QList<BuildStep *> m_buildQueue;
Daniel Teske's avatar
Daniel Teske committed
83
    QList<bool> m_enabledState;
84
    QStringList m_stepNames;
85 86 87
    ProjectExplorerPlugin *m_projectExplorerPlugin;
    bool m_running;
    QFutureWatcher<bool> m_watcher;
88
    QFutureInterface<bool> m_futureInterfaceForAysnc;
89 90 91 92
    BuildStep *m_currentBuildStep;
    QString m_currentConfiguration;
    // used to decide if we are building a project to decide when to emit buildStateChanged(Project *)
    QHash<Project *, int>  m_activeBuildSteps;
Daniel Teske's avatar
Daniel Teske committed
93 94
    QHash<Target *, int> m_activeBuildStepsPerTarget;
    QHash<ProjectConfiguration *, int> m_activeBuildStepsPerProjectConfiguration;
95 96
    Project *m_previousBuildStepProject;
    // is set to true while canceling, so that nextBuildStep knows that the BuildStep finished because of canceling
Daniel Teske's avatar
Daniel Teske committed
97
    bool m_skipDisabled;
98
    bool m_canceling;
99 100
    bool m_doNotEnterEventLoop;
    QEventLoop *m_eventLoop;
101 102 103 104 105 106

    // Progress reporting to the progress manager
    int m_progress;
    int m_maxProgress;
    QFutureInterface<void> *m_progressFutureInterface;
    QFutureWatcher<void> m_progressWatcher;
107
    QWeakPointer<Core::FutureProgress> m_futureProgress;
108 109 110 111 112
};

BuildManagerPrivate::BuildManagerPrivate() :
    m_running(false)
  , m_previousBuildStepProject(0)
Daniel Teske's avatar
Daniel Teske committed
113
  , m_skipDisabled(false)
114
  , m_canceling(false)
115 116
  , m_doNotEnterEventLoop(false)
  , m_eventLoop(0)
117 118 119
  , m_maxProgress(0)
  , m_progressFutureInterface(0)
{
Friedemann Kleint's avatar
Friedemann Kleint committed
120 121
}

con's avatar
con committed
122
BuildManager::BuildManager(ProjectExplorerPlugin *parent)
123
    : QObject(parent), d(new BuildManagerPrivate)
con's avatar
con committed
124 125
{
    ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
126
    d->m_projectExplorerPlugin = parent;
con's avatar
con committed
127

128
    connect(&d->m_watcher, SIGNAL(finished()),
con's avatar
con committed
129 130
            this, SLOT(nextBuildQueue()));

131
    connect(&d->m_watcher, SIGNAL(progressValueChanged(int)),
132
            this, SLOT(progressChanged()));
133 134
    connect(&d->m_watcher, SIGNAL(progressTextChanged(QString)),
            this, SLOT(progressTextChanged()));
135
    connect(&d->m_watcher, SIGNAL(progressRangeChanged(int, int)),
136 137
            this, SLOT(progressChanged()));

138 139 140
    connect(parent->session(), SIGNAL(aboutToRemoveProject(ProjectExplorer::Project *)),
            this, SLOT(aboutToRemoveProject(ProjectExplorer::Project *)));

141 142
    d->m_outputWindow = new Internal::CompileOutputWindow(this);
    pm->addObject(d->m_outputWindow);
con's avatar
con committed
143

144 145 146
    d->m_taskHub = pm->getObject<TaskHub>();
    d->m_taskWindow = new Internal::TaskWindow(d->m_taskHub);
    pm->addObject(d->m_taskWindow);
con's avatar
con committed
147

148
    qRegisterMetaType<ProjectExplorer::BuildStep::OutputFormat>();
149
    qRegisterMetaType<ProjectExplorer::BuildStep::OutputNewlineSetting>();
150

151
    connect(d->m_taskWindow, SIGNAL(tasksChanged()),
152
            this, SLOT(updateTaskCount()));
con's avatar
con committed
153

154
    connect(d->m_taskWindow, SIGNAL(tasksCleared()),
dt's avatar
dt committed
155 156
            this,SIGNAL(tasksCleared()));

157
    connect(&d->m_progressWatcher, SIGNAL(canceled()),
con's avatar
con committed
158
            this, SLOT(cancel()));
159
    connect(&d->m_progressWatcher, SIGNAL(finished()),
160
            this, SLOT(finish()));
con's avatar
con committed
161 162
}

dt's avatar
dt committed
163 164
void BuildManager::extensionsInitialized()
{
165
    d->m_taskHub->addCategory(Core::Id(Constants::TASK_CATEGORY_COMPILE),
166
        tr("Compile", "Category for compiler issues listed under 'Issues'"));
167
    d->m_taskHub->addCategory(Core::Id(Constants::TASK_CATEGORY_BUILDSYSTEM),
168
        tr("Build System", "Category for build system issues listed under 'Issues'"));
dt's avatar
dt committed
169 170
}

con's avatar
con committed
171 172 173 174 175
BuildManager::~BuildManager()
{
    cancel();
    ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();

176 177
    pm->removeObject(d->m_taskWindow);
    delete d->m_taskWindow;
con's avatar
con committed
178

179 180
    pm->removeObject(d->m_outputWindow);
    delete d->m_outputWindow;
hjk's avatar
hjk committed
181 182

    delete d;
con's avatar
con committed
183 184
}

185 186
void BuildManager::aboutToRemoveProject(ProjectExplorer::Project *p)
{
187 188
    QHash<Project *, int>::iterator it = d->m_activeBuildSteps.find(p);
    QHash<Project *, int>::iterator end = d->m_activeBuildSteps.end();
189 190 191 192 193 194 195 196
    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
197 198 199
bool BuildManager::isBuilding() const
{
    // we are building even if we are not running yet
200
    return !d->m_buildQueue.isEmpty() || d->m_running;
con's avatar
con committed
201 202
}

203 204 205
int BuildManager::getErrorTaskCount() const
{
    const int errors =
206 207
            d->m_taskWindow->errorTaskCount(Core::Id(Constants::TASK_CATEGORY_BUILDSYSTEM))
            + d->m_taskWindow->errorTaskCount(Core::Id(Constants::TASK_CATEGORY_COMPILE));
208 209 210
    return errors;
}

con's avatar
con committed
211 212
void BuildManager::cancel()
{
213 214 215
    if (d->m_running) {
        d->m_canceling = true;
        d->m_watcher.cancel();
216 217 218 219 220 221 222 223 224 225 226 227 228 229
        if (d->m_currentBuildStep->runInGuiThread()) {
            // This is evil. A nested event loop.
            d->m_currentBuildStep->cancel();
            if (d->m_doNotEnterEventLoop) {
                d->m_doNotEnterEventLoop = false;
            } else {
                d->m_eventLoop = new QEventLoop;
                d->m_eventLoop->exec();
                delete d->m_eventLoop;
                d->m_eventLoop = 0;
            }
        } else {
            d->m_watcher.waitForFinished();
        }
con's avatar
con committed
230 231 232 233 234 235 236

        // 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()));

237
        disconnectOutput(d->m_currentBuildStep);
Daniel Teske's avatar
Daniel Teske committed
238
        decrementActiveBuildSteps(d->m_currentBuildStep);
con's avatar
con committed
239

240
        d->m_progressFutureInterface->setProgressValueAndText(d->m_progress*100, tr("Build/Deployment canceled")); //TODO NBS fix in qtconcurrent
con's avatar
con committed
241 242 243 244 245
        clearBuildQueue();
    }
    return;
}

246 247
void BuildManager::updateTaskCount()
{
hjk's avatar
hjk committed
248
    Core::ProgressManager *progressManager = Core::ICore::progressManager();
249
    const int errors = getErrorTaskCount();
250
    if (errors > 0) {
251
        progressManager->setApplicationLabel(QString::number(errors));
252
    } else {
253
        progressManager->setApplicationLabel(QString());
254 255 256 257 258 259
    }
    emit tasksChanged();
}

void BuildManager::finish()
{
hjk's avatar
hjk committed
260
    QApplication::alert(Core::ICore::mainWindow(), 3000);
261 262
}

con's avatar
con committed
263 264
void BuildManager::emitCancelMessage()
{
265
    addToOutputWindow(tr("Canceled build/deployment."), BuildStep::ErrorMessageOutput);
con's avatar
con committed
266 267 268 269
}

void BuildManager::clearBuildQueue()
{
270
    foreach (BuildStep *bs, d->m_buildQueue) {
Daniel Teske's avatar
Daniel Teske committed
271
        decrementActiveBuildSteps(bs);
272
        disconnectOutput(bs);
273
    }
con's avatar
con committed
274

275
    d->m_stepNames.clear();
276
    d->m_buildQueue.clear();
Daniel Teske's avatar
Daniel Teske committed
277
    d->m_enabledState.clear();
278 279 280
    d->m_running = false;
    d->m_previousBuildStepProject = 0;
    d->m_currentBuildStep = 0;
con's avatar
con committed
281

282 283 284 285 286
    d->m_progressFutureInterface->reportCanceled();
    d->m_progressFutureInterface->reportFinished();
    d->m_progressWatcher.setFuture(QFuture<void>());
    delete d->m_progressFutureInterface;
    d->m_progressFutureInterface = 0;
287
    d->m_futureProgress.clear();
288
    d->m_maxProgress = 0;
con's avatar
con committed
289 290 291 292 293 294 295

    emit buildQueueFinished(false);
}


void BuildManager::toggleOutputWindow()
{
296
    d->m_outputWindow->toggle(false);
con's avatar
con committed
297 298 299 300
}

void BuildManager::showTaskWindow()
{
301
    d->m_taskWindow->popup(false);
con's avatar
con committed
302 303 304 305
}

void BuildManager::toggleTaskWindow()
{
306
    d->m_taskWindow->toggle(false);
con's avatar
con committed
307 308 309 310
}

bool BuildManager::tasksAvailable() const
{
311
    const int count =
312 313
            d->m_taskWindow->taskCount(Core::Id(Constants::TASK_CATEGORY_BUILDSYSTEM))
            + d->m_taskWindow->taskCount(Core::Id(Constants::TASK_CATEGORY_COMPILE));
314
    return count > 0;
con's avatar
con committed
315 316
}

317
void BuildManager::startBuildQueue(const QStringList &preambleMessage)
con's avatar
con committed
318
{
319
    if (d->m_buildQueue.isEmpty()) {
320
        emit buildQueueFinished(true);
321
        return;
322
    }
323
    if (!d->m_running) {
con's avatar
con committed
324
        // Progress Reporting
hjk's avatar
hjk committed
325
        Core::ProgressManager *progressManager = Core::ICore::progressManager();
326 327 328
        d->m_progressFutureInterface = new QFutureInterface<void>;
        d->m_progressWatcher.setFuture(d->m_progressFutureInterface->future());
        d->m_outputWindow->clearContents();
329 330
        foreach (const QString &str, preambleMessage)
            addToOutputWindow(str, BuildStep::MessageOutput, BuildStep::DontAppendNewline);
331 332
        d->m_taskHub->clearTasks(Core::Id(Constants::TASK_CATEGORY_COMPILE));
        d->m_taskHub->clearTasks(Core::Id(Constants::TASK_CATEGORY_BUILDSYSTEM));
333
        progressManager->setApplicationLabel(QString());
334
        d->m_futureProgress = QWeakPointer<Core::FutureProgress>(progressManager->addTask(d->m_progressFutureInterface->future(),
335 336
              QString(),
              QLatin1String(Constants::TASK_BUILD),
337 338 339
              Core::ProgressManager::KeepOnFinish | Core::ProgressManager::ShowInApplicationIcon));
        connect(d->m_futureProgress.data(), SIGNAL(clicked()), this, SLOT(showBuildResults()));
        d->m_futureProgress.data()->setWidget(new Internal::BuildProgress(d->m_taskWindow));
340 341
        d->m_progress = 0;
        d->m_progressFutureInterface->setProgressRange(0, d->m_maxProgress * 100);
con's avatar
con committed
342

343 344 345
        d->m_running = true;
        d->m_canceling = false;
        d->m_progressFutureInterface->reportStarted();
con's avatar
con committed
346 347 348
        nextStep();
    } else {
        // Already running
349 350
        d->m_progressFutureInterface->setProgressRange(0, d->m_maxProgress * 100);
        d->m_progressFutureInterface->setProgressValueAndText(d->m_progress*100, msgProgress(d->m_progress, d->m_maxProgress));
con's avatar
con committed
351 352 353 354 355
    }
}

void BuildManager::showBuildResults()
{
356
    if (tasksAvailable())
con's avatar
con committed
357 358 359 360 361 362
        toggleTaskWindow();
    else
        toggleOutputWindow();
    //toggleTaskWindow();
}

363
void BuildManager::addToTaskWindow(const ProjectExplorer::Task &task)
con's avatar
con committed
364
{
365
    d->m_outputWindow->registerPositionOf(task);
dt's avatar
dt committed
366
    // Distribute to all others
367
    d->m_taskHub->addTask(task);
con's avatar
con committed
368 369
}

370 371
void BuildManager::addToOutputWindow(const QString &string, BuildStep::OutputFormat format,
    BuildStep::OutputNewlineSetting newLineSetting)
con's avatar
con committed
372
{
373 374 375 376 377 378
    QString stringToWrite;
    if (format == BuildStep::MessageOutput || format == BuildStep::ErrorMessageOutput) {
        stringToWrite = QTime::currentTime().toString();
        stringToWrite += QLatin1String(": ");
    }
    stringToWrite += string;
379 380 381
    if (newLineSetting == BuildStep::DoAppendNewline)
        stringToWrite += QLatin1Char('\n');
    d->m_outputWindow->appendText(stringToWrite, format);
con's avatar
con committed
382 383
}

384 385 386 387 388 389 390 391 392 393 394 395 396 397 398
void BuildManager::buildStepFinishedAsync()
{
    disconnect(d->m_currentBuildStep, SIGNAL(finished()),
               this, SLOT(buildStepFinishedAsync()));
    d->m_futureInterfaceForAysnc = QFutureInterface<bool>();
    if (d->m_canceling) {
        if (d->m_eventLoop)
            d->m_eventLoop->exit();
        else
            d->m_doNotEnterEventLoop = true;
    } else {
        nextBuildQueue();
    }
}

con's avatar
con committed
399 400
void BuildManager::nextBuildQueue()
{
401
    if (d->m_canceling)
con's avatar
con committed
402 403
        return;

404
    disconnectOutput(d->m_currentBuildStep);
405 406
    ++d->m_progress;
    d->m_progressFutureInterface->setProgressValueAndText(d->m_progress*100, msgProgress(d->m_progress, d->m_maxProgress));
Daniel Teske's avatar
Daniel Teske committed
407
    decrementActiveBuildSteps(d->m_currentBuildStep);
con's avatar
con committed
408

Daniel Teske's avatar
Daniel Teske committed
409
    bool result = d->m_skipDisabled || d->m_watcher.result();
con's avatar
con committed
410 411
    if (!result) {
        // Build Failure
412 413
        const QString projectName = d->m_currentBuildStep->project()->displayName();
        const QString targetName = d->m_currentBuildStep->target()->displayName();
414 415
        addToOutputWindow(tr("Error while building/deploying project %1 (target: %2)").arg(projectName, targetName), BuildStep::ErrorOutput);
        addToOutputWindow(tr("When executing step '%1'").arg(d->m_currentBuildStep->displayName()), BuildStep::ErrorOutput);
con's avatar
con committed
416
        // NBS TODO fix in qtconcurrent
417
        d->m_progressFutureInterface->setProgressValueAndText(d->m_progress*100, tr("Error while building/deploying project %1 (target: %2)").arg(projectName, targetName));
con's avatar
con committed
418 419 420 421 422 423 424 425
    }

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

426 427
void BuildManager::progressChanged()
{
428
    if (!d->m_progressFutureInterface)
429
        return;
430
    int range = d->m_watcher.progressMaximum() - d->m_watcher.progressMinimum();
431
    if (range != 0) {
432
        int percent = (d->m_watcher.progressValue() - d->m_watcher.progressMinimum()) * 100 / range;
433 434
        d->m_progressFutureInterface->setProgressValueAndText(d->m_progress * 100 + percent, msgProgress(d->m_progress, d->m_maxProgress)
                                                              + QLatin1Char('\n') + d->m_watcher.progressText());
435 436 437
    }
}

438 439 440 441 442 443
void BuildManager::progressTextChanged()
{
    int range = d->m_watcher.progressMaximum() - d->m_watcher.progressMinimum();
    int percent = 0;
    if (range != 0)
        percent = (d->m_watcher.progressValue() - d->m_watcher.progressMinimum()) * 100 / range;
444 445
    d->m_progressFutureInterface->setProgressValueAndText(d->m_progress*100 + percent, msgProgress(d->m_progress, d->m_maxProgress) +
                                                          QLatin1Char('\n') + d->m_watcher.progressText());
446 447
}

con's avatar
con committed
448 449
void BuildManager::nextStep()
{
450 451 452
    if (!d->m_buildQueue.empty()) {
        d->m_currentBuildStep = d->m_buildQueue.front();
        d->m_buildQueue.pop_front();
453
        QString name = d->m_stepNames.takeFirst();
Daniel Teske's avatar
Daniel Teske committed
454
        d->m_skipDisabled = !d->m_enabledState.takeFirst();
455 456
        if (d->m_futureProgress)
            d->m_futureProgress.data()->setTitle(name);
con's avatar
con committed
457

458 459
        if (d->m_currentBuildStep->project() != d->m_previousBuildStepProject) {
            const QString projectName = d->m_currentBuildStep->project()->displayName();
460
            addToOutputWindow(tr("Running steps for project %1...")
461
                              .arg(projectName), BuildStep::MessageOutput);
462
            d->m_previousBuildStepProject = d->m_currentBuildStep->project();
con's avatar
con committed
463
        }
Daniel Teske's avatar
Daniel Teske committed
464 465 466 467 468 469 470 471

        if (d->m_skipDisabled) {
            addToOutputWindow(tr("Skipping disabled step %1.")
                              .arg(d->m_currentBuildStep->displayName()), BuildStep::MessageOutput);
            nextBuildQueue();
            return;
        }

472 473 474 475 476 477 478 479
        if (d->m_currentBuildStep->runInGuiThread()) {
            connect (d->m_currentBuildStep, SIGNAL(finished()),
                     this, SLOT(buildStepFinishedAsync()));
            d->m_watcher.setFuture(d->m_futureInterfaceForAysnc.future());
            d->m_currentBuildStep->run(d->m_futureInterfaceForAysnc);
        } else {
            d->m_watcher.setFuture(QtConcurrent::run(&BuildStep::run, d->m_currentBuildStep));
        }
con's avatar
con committed
480
    } else {
481 482 483 484 485 486 487 488
        d->m_running = false;
        d->m_previousBuildStepProject = 0;
        d->m_progressFutureInterface->reportFinished();
        d->m_progressWatcher.setFuture(QFuture<void>());
        d->m_currentBuildStep = 0;
        delete d->m_progressFutureInterface;
        d->m_progressFutureInterface = 0;
        d->m_maxProgress = 0;
con's avatar
con committed
489 490 491 492
        emit buildQueueFinished(true);
    }
}

Daniel Teske's avatar
Daniel Teske committed
493
bool BuildManager::buildQueueAppend(QList<BuildStep *> steps, QStringList names)
con's avatar
con committed
494
{
dt's avatar
dt committed
495 496 497 498 499
    int count = steps.size();
    bool init = true;
    int i = 0;
    for (; i < count; ++i) {
        BuildStep *bs = steps.at(i);
500 501
        connect(bs, SIGNAL(addTask(ProjectExplorer::Task)),
                this, SLOT(addToTaskWindow(ProjectExplorer::Task)));
502 503
        connect(bs, SIGNAL(addOutput(QString, ProjectExplorer::BuildStep::OutputFormat, ProjectExplorer::BuildStep::OutputNewlineSetting)),
                this, SLOT(addToOutputWindow(QString, ProjectExplorer::BuildStep::OutputFormat, ProjectExplorer::BuildStep::OutputNewlineSetting)));
Daniel Teske's avatar
Daniel Teske committed
504 505 506 507 508
        if (bs->enabled()) {
            init = bs->init();
            if (!init)
                break;
        }
dt's avatar
dt committed
509
    }
510
    if (!init) {
dt's avatar
dt committed
511 512 513 514
        BuildStep *bs = steps.at(i);

        // cleaning up
        // print something for the user
515
        const QString projectName = bs->project()->displayName();
Tobias Hunger's avatar
Tobias Hunger committed
516
        const QString targetName = bs->target()->displayName();
517 518
        addToOutputWindow(tr("Error while building/deploying project %1 (target: %2)").arg(projectName, targetName), BuildStep::ErrorOutput);
        addToOutputWindow(tr("When executing step '%1'").arg(bs->displayName()), BuildStep::ErrorOutput);
dt's avatar
dt committed
519 520

        // disconnect the buildsteps again
521 522
        for (int j = 0; j <= i; ++j)
            disconnectOutput(steps.at(j));
523 524 525
        return false;
    }

dt's avatar
dt committed
526 527
    // Everthing init() well
    for (i = 0; i < count; ++i) {
528 529
        ++d->m_maxProgress;
        d->m_buildQueue.append(steps.at(i));
530
        d->m_stepNames.append(names.at(i));
Daniel Teske's avatar
Daniel Teske committed
531
        d->m_enabledState.append(steps.at(i)->enabled());
Daniel Teske's avatar
Daniel Teske committed
532
        incrementActiveBuildSteps(steps.at(i));
dt's avatar
dt committed
533
    }
534
    return true;
con's avatar
con committed
535 536
}

537
bool BuildManager::buildList(BuildStepList *bsl, const QString &stepListName)
con's avatar
con committed
538
{
539
    return buildLists(QList<BuildStepList *>() << bsl, QStringList() << stepListName);
con's avatar
con committed
540 541
}

542
bool BuildManager::buildLists(QList<BuildStepList *> bsls, const QStringList &stepListNames, const QStringList &preambelMessage)
con's avatar
con committed
543
{
dt's avatar
dt committed
544
    QList<BuildStep *> steps;
Tobias Hunger's avatar
Tobias Hunger committed
545 546
    foreach(BuildStepList *list, bsls)
        steps.append(list->steps());
dt's avatar
dt committed
547

548 549 550 551 552 553 554 555 556
    QStringList names;
    names.reserve(steps.size());
    for (int i = 0; i < bsls.size(); ++i) {
        for (int j = 0; j < bsls.at(i)->steps().size(); ++j) {
            names.append(stepListNames.at(i));
        }
    }

    bool success = buildQueueAppend(steps, names);
dt's avatar
dt committed
557
    if (!success) {
558
        d->m_outputWindow->popup(false);
559
        return false;
con's avatar
con committed
560
    }
dt's avatar
dt committed
561

562
    if (ProjectExplorerPlugin::instance()->projectExplorerSettings().showCompilerOutput)
563
        d->m_outputWindow->popup(false);
564
    startBuildQueue(preambelMessage);
565
    return true;
con's avatar
con committed
566 567
}

568
void BuildManager::appendStep(BuildStep *step, const QString &name)
con's avatar
con committed
569
{
570
    bool success = buildQueueAppend(QList<BuildStep *>() << step, QStringList() << name);
dt's avatar
dt committed
571
    if (!success) {
572
        d->m_outputWindow->popup(false);
dt's avatar
dt committed
573 574 575
        return;
    }
    if (ProjectExplorerPlugin::instance()->projectExplorerSettings().showCompilerOutput)
576
        d->m_outputWindow->popup(false);
con's avatar
con committed
577 578 579
    startBuildQueue();
}

Daniel Teske's avatar
Daniel Teske committed
580 581 582 583 584 585 586 587 588 589
template <class T>
int count(const QHash<T *, int> &hash, T *key)
{
    typename QHash<T *, int>::const_iterator it = hash.find(key);
    typename QHash<T *, int>::const_iterator end = hash.end();
    if (it != end)
        return *it;
    return 0;
}

con's avatar
con committed
590 591
bool BuildManager::isBuilding(Project *pro)
{
Daniel Teske's avatar
Daniel Teske committed
592 593 594 595 596 597 598 599 600 601 602
    return count(d->m_activeBuildSteps, pro) > 0;
}

bool BuildManager::isBuilding(Target *t)
{
    return count(d->m_activeBuildStepsPerTarget, t) > 0;
}

bool BuildManager::isBuilding(ProjectConfiguration *p)
{
    return count(d->m_activeBuildStepsPerProjectConfiguration, p) > 0;
con's avatar
con committed
603 604
}

605 606
bool BuildManager::isBuilding(BuildStep *step)
{
607
    return (d->m_currentBuildStep == step) || d->m_buildQueue.contains(step);
608 609
}

Daniel Teske's avatar
Daniel Teske committed
610
template <class T> bool increment(QHash<T *, int> &hash, T *key)
con's avatar
con committed
611
{
Daniel Teske's avatar
Daniel Teske committed
612 613
    typename QHash<T *, int>::iterator it = hash.find(key);
    typename QHash<T *, int>::iterator end = hash.end();
con's avatar
con committed
614
    if (it == end) {
Daniel Teske's avatar
Daniel Teske committed
615 616
        hash.insert(key, 1);
        return true;
con's avatar
con committed
617 618
    } else if (*it == 0) {
        ++*it;
Daniel Teske's avatar
Daniel Teske committed
619
        return true;
con's avatar
con committed
620 621 622
    } else {
        ++*it;
    }
Daniel Teske's avatar
Daniel Teske committed
623
    return false;
con's avatar
con committed
624 625
}

Daniel Teske's avatar
Daniel Teske committed
626
template <class T> bool decrement(QHash<T *, int> &hash, T *key)
con's avatar
con committed
627
{
Daniel Teske's avatar
Daniel Teske committed
628 629
    typename QHash<T *, int>::iterator it = hash.find(key);
    typename QHash<T *, int>::iterator end = hash.end();
con's avatar
con committed
630
    if (it == end) {
Daniel Teske's avatar
Daniel Teske committed
631
        // Can't happen
con's avatar
con committed
632 633
    } else if (*it == 1) {
        --*it;
Daniel Teske's avatar
Daniel Teske committed
634
        return true;
con's avatar
con committed
635 636 637
    } else {
        --*it;
    }
Daniel Teske's avatar
Daniel Teske committed
638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654
    return false;
}

void BuildManager::incrementActiveBuildSteps(BuildStep *bs)
{
    increment<ProjectConfiguration>(d->m_activeBuildStepsPerProjectConfiguration, bs->projectConfiguration());
    increment<Target>(d->m_activeBuildStepsPerTarget, bs->target());
    if (increment<Project>(d->m_activeBuildSteps, bs->project()))
        emit buildStateChanged(bs->project());
}

void BuildManager::decrementActiveBuildSteps(BuildStep *bs)
{
    decrement<ProjectConfiguration>(d->m_activeBuildStepsPerProjectConfiguration, bs->projectConfiguration());
    decrement<Target>(d->m_activeBuildStepsPerTarget, bs->target());
    if (decrement<Project>(d->m_activeBuildSteps, bs->project()))
        emit buildStateChanged(bs->project());
con's avatar
con committed
655
}
656

657 658 659 660 661 662 663 664 665 666
void BuildManager::disconnectOutput(BuildStep *bs)
{
    disconnect(bs, SIGNAL(addTask(ProjectExplorer::Task)),
               this, SLOT(addToTaskWindow(ProjectExplorer::Task)));
    disconnect(bs, SIGNAL(addOutput(QString, ProjectExplorer::BuildStep::OutputFormat,
        ProjectExplorer::BuildStep::OutputNewlineSetting)),
        this, SLOT(addToOutputWindow(QString, ProjectExplorer::BuildStep::OutputFormat,
            ProjectExplorer::BuildStep::OutputNewlineSetting)));
}

667
} // namespace ProjectExplorer