Newer
Older
/**************************************************************************
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
** Contact: Nokia Corporation (qt-info@nokia.com)
** 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
**************************************************************************/
#include "buildstep.h"
#include "compileoutputwindow.h"
#include "projectexplorerconstants.h"
#include "project.h"
#include "projectexplorersettings.h"
#include <coreplugin/icore.h>
#include <coreplugin/progressmanager/progressmanager.h>
#include <extensionsystem/pluginmanager.h>
#include <utils/qtcassert.h>
#include <qtconcurrent/QtConcurrentTools>
#include <QtGui/QHeaderView>
#include <QtGui/QIcon>
#include <QtGui/QLabel>
#include <QtGui/QApplication>
#include <QtGui/QMainWindow>
using namespace ProjectExplorer;
using namespace ProjectExplorer::Internal;
static inline QString msgProgress(int n, int total)
{
return BuildManager::tr("Finished %n of %1 build steps", 0, n).arg(total);
}
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()));
connect(&m_watcher, SIGNAL(progressValueChanged(int)),
this, SLOT(progressChanged()));
connect(&m_watcher, SIGNAL(progressRangeChanged(int, int)),
this, SLOT(progressChanged()));
m_outputWindow = new CompileOutputWindow(this);
pm->addObject(m_outputWindow);
m_taskWindow = new TaskWindow;
pm->addObject(m_taskWindow);
m_taskWindow->addCategory(Constants::TASK_CATEGORY_COMPILE, tr("Compile", "Category for compiler isses listened under 'Build Issues'"));
m_taskWindow->addCategory(Constants::TASK_CATEGORY_BUILDSYSTEM, tr("Buildsystem", "Category for build system isses listened under 'Build Issues'"));
this, SLOT(updateTaskCount()));
connect(&m_progressWatcher, SIGNAL(canceled()),
this, SLOT(cancel()));
connect(&m_progressWatcher, SIGNAL(finished()),
this, SLOT(finish()));
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
133
134
}
BuildManager::~BuildManager()
{
cancel();
ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
pm->removeObject(m_taskWindow);
delete m_taskWindow;
pm->removeObject(m_outputWindow);
delete m_outputWindow;
}
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()));
disconnect(m_currentBuildStep, SIGNAL(addTask(ProjectExplorer::TaskWindow::Task)),
this, SLOT(addToTaskWindow(ProjectExplorer::TaskWindow::Task)));
disconnect(m_currentBuildStep, SIGNAL(addOutput(QString)),
decrementActiveBuildSteps(m_currentBuildStep->buildConfiguration()->project());
m_progressFutureInterface->setProgressValueAndText(m_progress*100, "Build canceled"); //TODO NBS fix in qtconcurrent
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);
}
void BuildManager::emitCancelMessage()
{
emit addToOutputWindow(tr("<font color=\"#ff0000\">Canceled build.</font>"));
}
void BuildManager::clearBuildQueue()
{
foreach (BuildStep * bs, m_buildQueue)
decrementActiveBuildSteps(bs->buildConfiguration()->project());
m_buildQueue.clear();
m_running = false;
m_previousBuildStepProject = 0;
m_progressFutureInterface->reportCanceled();
m_progressFutureInterface->reportFinished();
m_progressWatcher.setFuture(QFuture<void>());
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
{
return m_taskWindow->taskCount() > 0;
}
void BuildManager::gotoTaskWindow()
{
m_taskWindow->popup(true);
}
void BuildManager::startBuildQueue()
{
if (m_buildQueue.isEmpty()) {
emit buildQueueFinished(true);
Core::ProgressManager *progressManager = Core::ICore::instance()->progressManager();
m_progressFutureInterface = new QFutureInterface<void>;
m_progressWatcher.setFuture(m_progressFutureInterface->future());
progressManager->setApplicationLabel("");
Core::FutureProgress *progress = progressManager->addTask(m_progressFutureInterface->future(),
tr("Build"),
Constants::TASK_BUILD,
Core::ProgressManager::KeepOnFinish | Core::ProgressManager::ShowInApplicationIcon);
connect(progress, SIGNAL(clicked()), this, SLOT(showBuildResults()));
progress->setWidget(new BuildProgress(m_taskWindow));
m_progress = 0;
m_progressFutureInterface->setProgressRange(0, m_maxProgress * 100);
m_running = true;
m_canceling = false;
m_progressFutureInterface->reportStarted();
m_outputWindow->clearContents();
m_taskWindow->clearTasks(Constants::TASK_CATEGORY_COMPILE);
m_progressFutureInterface->setProgressRange(0, m_maxProgress * 100);
m_progressFutureInterface->setProgressValueAndText(m_progress*100, msgProgress(m_progress, m_maxProgress));
if (m_taskWindow->taskCount() != 0)
toggleTaskWindow();
else
toggleOutputWindow();
//toggleTaskWindow();
}
void BuildManager::addToTaskWindow(const ProjectExplorer::TaskWindow::Task &task)
m_taskWindow->addTask(task);
}
void BuildManager::addToOutputWindow(const QString &string)
{
m_outputWindow->appendText(string);
}
void BuildManager::nextBuildQueue()
{
if (m_canceling)
return;
disconnect(m_currentBuildStep, SIGNAL(addTask(ProjectExplorer::TaskWindow::Task)),
this, SLOT(addToTaskWindow(ProjectExplorer::TaskWindow::Task)));
disconnect(m_currentBuildStep, SIGNAL(addOutput(QString)),
m_progressFutureInterface->setProgressValueAndText(m_progress*100, msgProgress(m_progress, m_maxProgress));
bool result = m_watcher.result();
if (!result) {
// Build Failure
const QString projectName = m_currentBuildStep->buildConfiguration()->project()->displayName();
addToOutputWindow(tr("<font color=\"#ff0000\">Error while building project %1</font>").arg(projectName));
addToOutputWindow(tr("<font color=\"#ff0000\">When executing build step '%1'</font>").arg(m_currentBuildStep->displayName()));
// NBS TODO fix in qtconcurrent
m_progressFutureInterface->setProgressValueAndText(m_progress*100, tr("Error while building project %1").arg(projectName));
decrementActiveBuildSteps(m_currentBuildStep->buildConfiguration()->project());
if (result)
nextStep();
else
clearBuildQueue();
}
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);
}
}
void BuildManager::nextStep()
{
if (!m_buildQueue.empty()) {
m_currentBuildStep = m_buildQueue.front();
m_buildQueue.pop_front();
connect(m_currentBuildStep, SIGNAL(addTask(ProjectExplorer::TaskWindow::Task)),
this, SLOT(addToTaskWindow(ProjectExplorer::TaskWindow::Task)));
connect(m_currentBuildStep, SIGNAL(addOutput(QString)),
bool init = m_currentBuildStep->init();
const QString projectName = m_currentBuildStep->buildConfiguration()->project()->displayName();
addToOutputWindow(tr("<font color=\"#ff0000\">Error while building project %1</font>").arg(projectName));
addToOutputWindow(tr("<font color=\"#ff0000\">When executing build step '%1'</font>").arg(m_currentBuildStep->displayName()));
cancel();
return;
}
if (m_currentBuildStep->buildConfiguration()->project() != m_previousBuildStepProject) {
const QString projectName = m_currentBuildStep->buildConfiguration()->project()->displayName();
addToOutputWindow(tr("<b>Running build steps for project %2...</b>")
.arg(projectName));
m_previousBuildStepProject = m_currentBuildStep->buildConfiguration()->project();
}
m_watcher.setFuture(QtConcurrent::run(&BuildStep::run, m_currentBuildStep));
} else {
m_running = false;
m_progressWatcher.setFuture(QFuture<void>());
delete m_progressFutureInterface;
m_progressFutureInterface = 0;
m_maxProgress = 0;
emit buildQueueFinished(true);
}
}
void BuildManager::buildQueueAppend(BuildStep * bs)
incrementActiveBuildSteps(bs->buildConfiguration()->project());
void BuildManager::buildProjects(const QList<BuildConfiguration *> &configurations)
foreach(BuildConfiguration *bc, configurations) {
QList<BuildStep *> buildSteps = bc->buildSteps();
foreach (BuildStep *bs, buildSteps) {
buildQueueAppend(bs);
if (ProjectExplorerPlugin::instance()->projectExplorerSettings().showCompilerOutput)
m_outputWindow->popup(false);
startBuildQueue();
void BuildManager::cleanProjects(const QList<BuildConfiguration *> &configurations)
foreach(BuildConfiguration *bc, configurations) {
QList<BuildStep *> cleanSteps = bc->cleanSteps();
foreach (BuildStep *bs, cleanSteps) {
buildQueueAppend(bs);
if (ProjectExplorerPlugin::instance()->projectExplorerSettings().showCompilerOutput)
m_outputWindow->popup(false);
startBuildQueue();
void BuildManager::buildProject(BuildConfiguration *configuration)
buildProjects(QList<BuildConfiguration *>() << configuration);
void BuildManager::cleanProject(BuildConfiguration *configuration)
cleanProjects(QList<BuildConfiguration *>() << configuration);
void BuildManager::appendStep(BuildStep *step)
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
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;
}
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) {
Q_ASSERT(false && "BuildManager m_activeBuildSteps says project is not building, but apparently a build step was still in the queue.");