Skip to content
Snippets Groups Projects
Select Git revision
  • master
1 result

.project

Blame
  • buildmanager.cpp 14.12 KiB
    /**************************************************************************
    **
    ** This file is part of Qt Creator
    **
    ** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
    **
    ** Contact: Nokia Corporation (qt-info@nokia.com)
    **
    ** 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
    ** contact the sales department at http://qt.nokia.com/contact.
    **
    **************************************************************************/
    
    #include "buildmanager.h"
    
    #include "buildprogress.h"
    #include "buildstep.h"
    #include "compileoutputwindow.h"
    #include "projectexplorerconstants.h"
    #include "projectexplorer.h"
    #include "project.h"
    #include "projectexplorersettings.h"
    #include "taskwindow.h"
    #include "buildconfiguration.h"
    
    #include <coreplugin/icore.h>
    #include <coreplugin/progressmanager/progressmanager.h>
    #include <coreplugin/progressmanager/futureprogress.h>
    #include <extensionsystem/pluginmanager.h>
    #include <utils/qtcassert.h>
    
    #include <QtCore/QDir>
    #include <QtCore/QTimer>
    
    #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'"));
    
        connect(m_taskWindow, SIGNAL(tasksChanged()),
                this, SLOT(updateTaskCount()));
    
        connect(&m_progressWatcher, SIGNAL(canceled()),
                this, SLOT(cancel()));
        connect(&m_progressWatcher, SIGNAL(finished()),
                this, SLOT(finish()));
    }
    
    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)),
                       this, SLOT(addToOutputWindow(QString)));
            decrementActiveBuildSteps(m_currentBuildStep->buildConfiguration()->project());
    
            m_progressFutureInterface->setProgressValueAndText(m_progress*100, "Build canceled"); //TODO NBS fix in qtconcurrent
            clearBuildQueue();
        }
        return;
    }
    
    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);
            return;
        }
        if (!m_running) {
            // Progress Reporting
            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);
            nextStep();
        } else {
            // Already running
            m_progressFutureInterface->setProgressRange(0, m_maxProgress * 100);
            m_progressFutureInterface->setProgressValueAndText(m_progress*100, msgProgress(m_progress, m_maxProgress));
        }
    }
    
    void BuildManager::showBuildResults()
    {
        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)),
                   this, SLOT(addToOutputWindow(QString)));
    
        ++m_progress;
        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)),
                    this, SLOT(addToOutputWindow(QString)));
    
            bool init = m_currentBuildStep->init();
            if (!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_previousBuildStepProject = 0;        
            m_progressFutureInterface->reportFinished();
            m_progressWatcher.setFuture(QFuture<void>());
            delete m_progressFutureInterface;
            m_progressFutureInterface = 0;
            m_maxProgress = 0;
            emit buildQueueFinished(true);
        }
    }
    
    void BuildManager::buildQueueAppend(BuildStep * bs)
    {
        m_buildQueue.append(bs);
        ++m_maxProgress;
        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)
    {
        buildQueueAppend(step);
        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.");
        } else if (*it == 1) {
            --*it;
            emit buildStateChanged(pro);
        } else {
            --*it;
        }
    }