OSDN Git Service

Update license.
[qt-creator-jp/qt-creator-jp.git] / src / plugins / projectexplorer / buildmanager.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
8 **
9 **
10 ** GNU Lesser General Public License Usage
11 **
12 ** This file may be used under the terms of the GNU Lesser General Public
13 ** License version 2.1 as published by the Free Software Foundation and
14 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
15 ** Please review the following information to ensure the GNU Lesser General
16 ** Public License version 2.1 requirements will be met:
17 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 **
19 ** In addition, as a special exception, Nokia gives you certain additional
20 ** rights. These rights are described in the Nokia Qt LGPL Exception
21 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 **
23 ** Other Usage
24 **
25 ** Alternatively, this file may be used in accordance with the terms and
26 ** conditions contained in a signed written agreement between you and Nokia.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **************************************************************************/
32
33 #include "buildmanager.h"
34
35 #include "buildprogress.h"
36 #include "buildsteplist.h"
37 #include "compileoutputwindow.h"
38 #include "projectexplorerconstants.h"
39 #include "projectexplorer.h"
40 #include "project.h"
41 #include "projectexplorersettings.h"
42 #include "target.h"
43 #include "taskwindow.h"
44 #include "taskhub.h"
45 #include "buildconfiguration.h"
46
47 #include <coreplugin/icore.h>
48 #include <coreplugin/progressmanager/progressmanager.h>
49 #include <coreplugin/progressmanager/futureprogress.h>
50 #include <projectexplorer/session.h>
51 #include <extensionsystem/pluginmanager.h>
52 #include <utils/qtcassert.h>
53
54 #include <QtCore/QDir>
55 #include <QtCore/QTimer>
56 #include <QtCore/QMetaType>
57 #include <QtCore/QList>
58 #include <QtCore/QHash>
59 #include <QtCore/QFutureWatcher>
60
61 #include <qtconcurrent/QtConcurrentTools>
62
63 #include <QtGui/QApplication>
64 #include <QtGui/QMainWindow>
65
66 static inline QString msgProgress(int progress, int total)
67 {
68     return ProjectExplorer::BuildManager::tr("Finished %1 of %n build steps", 0, total).arg(progress);
69 }
70
71 namespace ProjectExplorer {
72 //NBS TODO this class has too many different variables which hold state:
73 // m_buildQueue, m_running, m_canceled, m_progress, m_maxProgress, m_activeBuildSteps and ...
74 // I might need to reduce that.
75 struct BuildManagerPrivate {
76     BuildManagerPrivate();
77
78     Internal::CompileOutputWindow *m_outputWindow;
79     TaskHub *m_taskHub;
80     Internal::TaskWindow *m_taskWindow;
81
82     QList<BuildStep *> m_buildQueue;
83     QStringList m_configurations; // the corresponding configuration to the m_buildQueue
84     ProjectExplorerPlugin *m_projectExplorerPlugin;
85     bool m_running;
86     QFutureWatcher<bool> m_watcher;
87     BuildStep *m_currentBuildStep;
88     QString m_currentConfiguration;
89     // used to decide if we are building a project to decide when to emit buildStateChanged(Project *)
90     QHash<Project *, int>  m_activeBuildSteps;
91     Project *m_previousBuildStepProject;
92     // is set to true while canceling, so that nextBuildStep knows that the BuildStep finished because of canceling
93     bool m_canceling;
94
95     // Progress reporting to the progress manager
96     int m_progress;
97     int m_maxProgress;
98     QFutureInterface<void> *m_progressFutureInterface;
99     QFutureWatcher<void> m_progressWatcher;
100 };
101
102 BuildManagerPrivate::BuildManagerPrivate() :
103     m_running(false)
104   , m_previousBuildStepProject(0)
105   , m_canceling(false)
106   , m_maxProgress(0)
107   , m_progressFutureInterface(0)
108 {
109 }
110
111 BuildManager::BuildManager(ProjectExplorerPlugin *parent)
112     : QObject(parent), d(new BuildManagerPrivate)
113 {
114     ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
115     d->m_projectExplorerPlugin = parent;
116
117     connect(&d->m_watcher, SIGNAL(finished()),
118             this, SLOT(nextBuildQueue()));
119
120     connect(&d->m_watcher, SIGNAL(progressValueChanged(int)),
121             this, SLOT(progressChanged()));
122     connect(&d->m_watcher, SIGNAL(progressTextChanged(QString)),
123             this, SLOT(progressTextChanged()));
124     connect(&d->m_watcher, SIGNAL(progressRangeChanged(int, int)),
125             this, SLOT(progressChanged()));
126
127     connect(parent->session(), SIGNAL(aboutToRemoveProject(ProjectExplorer::Project *)),
128             this, SLOT(aboutToRemoveProject(ProjectExplorer::Project *)));
129
130     d->m_outputWindow = new Internal::CompileOutputWindow(this);
131     pm->addObject(d->m_outputWindow);
132
133     d->m_taskHub = pm->getObject<TaskHub>();
134     d->m_taskWindow = new Internal::TaskWindow(d->m_taskHub);
135     pm->addObject(d->m_taskWindow);
136
137     qRegisterMetaType<ProjectExplorer::BuildStep::OutputFormat>();
138
139     connect(d->m_taskWindow, SIGNAL(tasksChanged()),
140             this, SLOT(updateTaskCount()));
141
142     connect(d->m_taskWindow, SIGNAL(tasksCleared()),
143             this,SIGNAL(tasksCleared()));
144
145     connect(&d->m_progressWatcher, SIGNAL(canceled()),
146             this, SLOT(cancel()));
147     connect(&d->m_progressWatcher, SIGNAL(finished()),
148             this, SLOT(finish()));
149 }
150
151 void BuildManager::extensionsInitialized()
152 {
153     d->m_taskHub->addCategory(Constants::TASK_CATEGORY_COMPILE, tr("Compile", "Category for compiler isses listened under 'Build Issues'"));
154     d->m_taskHub->addCategory(Constants::TASK_CATEGORY_BUILDSYSTEM, tr("Build System", "Category for build system isses listened under 'Build Issues'"));
155 }
156
157 BuildManager::~BuildManager()
158 {
159     cancel();
160     ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
161
162     pm->removeObject(d->m_taskWindow);
163     delete d->m_taskWindow;
164
165     pm->removeObject(d->m_outputWindow);
166     delete d->m_outputWindow;
167 }
168
169 void BuildManager::aboutToRemoveProject(ProjectExplorer::Project *p)
170 {
171     QHash<Project *, int>::iterator it = d->m_activeBuildSteps.find(p);
172     QHash<Project *, int>::iterator end = d->m_activeBuildSteps.end();
173     if (it != end && *it > 0) {
174         // We are building the project that's about to be removed.
175         // We cancel the whole queue, which isn't the nicest thing to do
176         // but a safe thing.
177         cancel();
178     }
179 }
180
181 bool BuildManager::isBuilding() const
182 {
183     // we are building even if we are not running yet
184     return !d->m_buildQueue.isEmpty() || d->m_running;
185 }
186
187 void BuildManager::cancel()
188 {
189     if (d->m_running) {
190         d->m_canceling = true;
191         d->m_watcher.cancel();
192         d->m_watcher.waitForFinished();
193
194         // The cancel message is added to the output window via a single shot timer
195         // since the canceling is likely to have generated new addToOutputWindow signals
196         // which are waiting in the event queue to be processed
197         // (And we want those to be before the cancel message.)
198         QTimer::singleShot(0, this, SLOT(emitCancelMessage()));
199
200         disconnect(d->m_currentBuildStep, SIGNAL(addTask(ProjectExplorer::Task)),
201                    this, SLOT(addToTaskWindow(ProjectExplorer::Task)));
202         disconnect(d->m_currentBuildStep, SIGNAL(addOutput(QString, ProjectExplorer::BuildStep::OutputFormat)),
203                    this, SLOT(addToOutputWindow(QString, ProjectExplorer::BuildStep::OutputFormat)));
204         decrementActiveBuildSteps(d->m_currentBuildStep->buildConfiguration()->target()->project());
205
206         d->m_progressFutureInterface->setProgressValueAndText(d->m_progress*100, tr("Build canceled")); //TODO NBS fix in qtconcurrent
207         clearBuildQueue();
208     }
209     return;
210 }
211
212 void BuildManager::updateTaskCount()
213 {
214     Core::ProgressManager *progressManager = Core::ICore::instance()->progressManager();
215     const int errors = d->m_taskWindow->errorTaskCount();
216     if (errors > 0) {
217         progressManager->setApplicationLabel(QString::number(errors));
218     } else {
219         progressManager->setApplicationLabel(QString());
220     }
221     emit tasksChanged();
222 }
223
224 void BuildManager::finish()
225 {
226     QApplication::alert(Core::ICore::instance()->mainWindow(), 3000);
227 }
228
229 void BuildManager::emitCancelMessage()
230 {
231     emit addToOutputWindow(tr("Canceled build."), BuildStep::ErrorMessageOutput);
232 }
233
234 void BuildManager::clearBuildQueue()
235 {
236     foreach (BuildStep *bs, d->m_buildQueue) {
237         decrementActiveBuildSteps(bs->buildConfiguration()->target()->project());
238         disconnect(bs, SIGNAL(addTask(ProjectExplorer::Task)),
239                    this, SLOT(addToTaskWindow(ProjectExplorer::Task)));
240         disconnect(bs, SIGNAL(addOutput(QString, ProjectExplorer::BuildStep::OutputFormat)),
241                    this, SLOT(addToOutputWindow(QString, ProjectExplorer::BuildStep::OutputFormat)));
242     }
243
244     d->m_buildQueue.clear();
245     d->m_running = false;
246     d->m_previousBuildStepProject = 0;
247     d->m_currentBuildStep = 0;
248
249     d->m_progressFutureInterface->reportCanceled();
250     d->m_progressFutureInterface->reportFinished();
251     d->m_progressWatcher.setFuture(QFuture<void>());
252     delete d->m_progressFutureInterface;
253     d->m_progressFutureInterface = 0;
254     d->m_maxProgress = 0;
255
256     emit buildQueueFinished(false);
257 }
258
259
260 void BuildManager::toggleOutputWindow()
261 {
262     d->m_outputWindow->toggle(false);
263 }
264
265 void BuildManager::showTaskWindow()
266 {
267     d->m_taskWindow->popup(false);
268 }
269
270 void BuildManager::toggleTaskWindow()
271 {
272     d->m_taskWindow->toggle(false);
273 }
274
275 bool BuildManager::tasksAvailable() const
276 {
277     return d->m_taskWindow->taskCount() > 0;
278 }
279
280 void BuildManager::startBuildQueue()
281 {
282     if (d->m_buildQueue.isEmpty()) {
283         emit buildQueueFinished(true);
284         return;
285     }
286     if (!d->m_running) {
287         // Progress Reporting
288         Core::ProgressManager *progressManager = Core::ICore::instance()->progressManager();
289         d->m_progressFutureInterface = new QFutureInterface<void>;
290         d->m_progressWatcher.setFuture(d->m_progressFutureInterface->future());
291         d->m_outputWindow->clearContents();
292         d->m_taskHub->clearTasks(Constants::TASK_CATEGORY_COMPILE);
293         d->m_taskHub->clearTasks(Constants::TASK_CATEGORY_BUILDSYSTEM);
294         progressManager->setApplicationLabel(QString());
295         Core::FutureProgress *progress = progressManager->addTask(d->m_progressFutureInterface->future(),
296               tr("Build"),
297               Constants::TASK_BUILD,
298               Core::ProgressManager::KeepOnFinish | Core::ProgressManager::ShowInApplicationIcon);
299         connect(progress, SIGNAL(clicked()), this, SLOT(showBuildResults()));
300         progress->setWidget(new Internal::BuildProgress(d->m_taskWindow));
301         d->m_progress = 0;
302         d->m_progressFutureInterface->setProgressRange(0, d->m_maxProgress * 100);
303
304         d->m_running = true;
305         d->m_canceling = false;
306         d->m_progressFutureInterface->reportStarted();
307         nextStep();
308     } else {
309         // Already running
310         d->m_progressFutureInterface->setProgressRange(0, d->m_maxProgress * 100);
311         d->m_progressFutureInterface->setProgressValueAndText(d->m_progress*100, msgProgress(d->m_progress, d->m_maxProgress));
312     }
313 }
314
315 void BuildManager::showBuildResults()
316 {
317     if (d->m_taskWindow->taskCount() != 0)
318         toggleTaskWindow();
319     else
320         toggleOutputWindow();
321     //toggleTaskWindow();
322 }
323
324 void BuildManager::addToTaskWindow(const ProjectExplorer::Task &task)
325 {
326     d->m_outputWindow->registerPositionOf(task);
327     // Distribute to all others
328     d->m_taskHub->addTask(task);
329 }
330
331 void BuildManager::addToOutputWindow(const QString &string, ProjectExplorer::BuildStep::OutputFormat format)
332 {
333     d->m_outputWindow->appendText(string, format);
334 }
335
336 void BuildManager::nextBuildQueue()
337 {
338     if (d->m_canceling)
339         return;
340
341     disconnect(d->m_currentBuildStep, SIGNAL(addTask(ProjectExplorer::Task)),
342                this, SLOT(addToTaskWindow(ProjectExplorer::Task)));
343     disconnect(d->m_currentBuildStep, SIGNAL(addOutput(QString, ProjectExplorer::BuildStep::OutputFormat)),
344                this, SLOT(addToOutputWindow(QString, ProjectExplorer::BuildStep::OutputFormat)));
345
346     ++d->m_progress;
347     d->m_progressFutureInterface->setProgressValueAndText(d->m_progress*100, msgProgress(d->m_progress, d->m_maxProgress));
348     decrementActiveBuildSteps(d->m_currentBuildStep->buildConfiguration()->target()->project());
349
350     bool result = d->m_watcher.result();
351     if (!result) {
352         // Build Failure
353         const QString projectName = d->m_currentBuildStep->buildConfiguration()->target()->project()->displayName();
354         const QString targetName = d->m_currentBuildStep->buildConfiguration()->target()->displayName();
355         addToOutputWindow(tr("Error while building project %1 (target: %2)").arg(projectName, targetName), BuildStep::ErrorOutput);
356         addToOutputWindow(tr("When executing build step '%1'").arg(d->m_currentBuildStep->displayName()), BuildStep::ErrorOutput);
357         // NBS TODO fix in qtconcurrent
358         d->m_progressFutureInterface->setProgressValueAndText(d->m_progress*100, tr("Error while building project %1 (target: %2)").arg(projectName, targetName));
359     }
360
361     if (result)
362         nextStep();
363     else
364         clearBuildQueue();
365 }
366
367 void BuildManager::progressChanged()
368 {
369     if (!d->m_progressFutureInterface)
370         return;
371     int range = d->m_watcher.progressMaximum() - d->m_watcher.progressMinimum();
372     if (range != 0) {
373         int percent = (d->m_watcher.progressValue() - d->m_watcher.progressMinimum()) * 100 / range;
374         d->m_progressFutureInterface->setProgressValueAndText(d->m_progress * 100 + percent, msgProgress(d->m_progress, d->m_maxProgress) + "\n" + d->m_watcher.progressText());
375     }
376 }
377
378 void BuildManager::progressTextChanged()
379 {
380     int range = d->m_watcher.progressMaximum() - d->m_watcher.progressMinimum();
381     int percent = 0;
382     if (range != 0)
383         percent = (d->m_watcher.progressValue() - d->m_watcher.progressMinimum()) * 100 / range;
384     d->m_progressFutureInterface->setProgressValueAndText(d->m_progress*100 + percent, msgProgress(d->m_progress, d->m_maxProgress) + "\n" + d->m_watcher.progressText());
385 }
386
387 void BuildManager::nextStep()
388 {
389     if (!d->m_buildQueue.empty()) {
390         d->m_currentBuildStep = d->m_buildQueue.front();
391         d->m_buildQueue.pop_front();
392
393         if (d->m_currentBuildStep->buildConfiguration()->target()->project() != d->m_previousBuildStepProject) {
394             const QString projectName = d->m_currentBuildStep->buildConfiguration()->target()->project()->displayName();
395             addToOutputWindow(tr("Running build steps for project %1...")
396                               .arg(projectName), BuildStep::MessageOutput);
397             d->m_previousBuildStepProject = d->m_currentBuildStep->buildConfiguration()->target()->project();
398         }
399         d->m_watcher.setFuture(QtConcurrent::run(&BuildStep::run, d->m_currentBuildStep));
400     } else {
401         d->m_running = false;
402         d->m_previousBuildStepProject = 0;
403         d->m_progressFutureInterface->reportFinished();
404         d->m_progressWatcher.setFuture(QFuture<void>());
405         d->m_currentBuildStep = 0;
406         delete d->m_progressFutureInterface;
407         d->m_progressFutureInterface = 0;
408         d->m_maxProgress = 0;
409         emit buildQueueFinished(true);
410     }
411 }
412
413 bool BuildManager::buildQueueAppend(QList<BuildStep *> steps)
414 {
415     int count = steps.size();
416     bool init = true;
417     int i = 0;
418     for (; i < count; ++i) {
419         BuildStep *bs = steps.at(i);
420         connect(bs, SIGNAL(addTask(ProjectExplorer::Task)),
421                 this, SLOT(addToTaskWindow(ProjectExplorer::Task)));
422         connect(bs, SIGNAL(addOutput(QString, ProjectExplorer::BuildStep::OutputFormat)),
423                 this, SLOT(addToOutputWindow(QString, ProjectExplorer::BuildStep::OutputFormat)));
424         init = bs->init();
425         if (!init)
426             break;
427     }
428     if (!init) {
429         BuildStep *bs = steps.at(i);
430
431         // cleaning up
432         // print something for the user
433         const QString projectName = bs->buildConfiguration()->target()->project()->displayName();
434         const QString targetName = bs->buildConfiguration()->target()->displayName();
435         addToOutputWindow(tr("Error while building project %1 (target: %2)").arg(projectName, targetName), BuildStep::ErrorOutput);
436         addToOutputWindow(tr("When executing build step '%1'").arg(bs->displayName()), BuildStep::ErrorOutput);
437
438         // disconnect the buildsteps again
439         for (int j = 0; j <= i; ++j) {
440             BuildStep *bs = steps.at(j);
441             disconnect(bs, SIGNAL(addTask(ProjectExplorer::Task)),
442                        this, SLOT(addToTaskWindow(ProjectExplorer::Task)));
443             disconnect(bs, SIGNAL(addOutput(QString, ProjectExplorer::BuildStep::OutputFormat)),
444                        this, SLOT(addToOutputWindow(QString, ProjectExplorer::BuildStep::OutputFormat)));
445         }
446         return false;
447     }
448
449     // Everthing init() well
450     for (i = 0; i < count; ++i) {
451         ++d->m_maxProgress;
452         d->m_buildQueue.append(steps.at(i));
453         incrementActiveBuildSteps(steps.at(i)->buildConfiguration()->target()->project());
454     }
455     return true;
456 }
457
458 bool BuildManager::buildList(BuildStepList *bsl)
459 {
460     return buildLists(QList<BuildStepList *>() << bsl);
461 }
462
463 bool BuildManager::buildLists(QList<BuildStepList *> bsls)
464 {
465     QList<BuildStep *> steps;
466     foreach(BuildStepList *list, bsls)
467         steps.append(list->steps());
468
469     bool success = buildQueueAppend(steps);
470     if (!success) {
471         d->m_outputWindow->popup(false);
472         return false;
473     }
474
475     if (ProjectExplorerPlugin::instance()->projectExplorerSettings().showCompilerOutput)
476         d->m_outputWindow->popup(false);
477     startBuildQueue();
478     return true;
479 }
480
481 void BuildManager::appendStep(BuildStep *step)
482 {
483     bool success = buildQueueAppend(QList<BuildStep *>() << step);
484     if (!success) {
485         d->m_outputWindow->popup(false);
486         return;
487     }
488     if (ProjectExplorerPlugin::instance()->projectExplorerSettings().showCompilerOutput)
489         d->m_outputWindow->popup(false);
490     startBuildQueue();
491 }
492
493 bool BuildManager::isBuilding(Project *pro)
494 {
495     QHash<Project *, int>::iterator it = d->m_activeBuildSteps.find(pro);
496     QHash<Project *, int>::iterator end = d->m_activeBuildSteps.end();
497     if (it == end || *it == 0)
498         return false;
499     else
500         return true;
501 }
502
503 bool BuildManager::isBuilding(BuildStep *step)
504 {
505     return (d->m_currentBuildStep == step) || d->m_buildQueue.contains(step);
506 }
507
508 void BuildManager::incrementActiveBuildSteps(Project *pro)
509 {
510     QHash<Project *, int>::iterator it = d->m_activeBuildSteps.find(pro);
511     QHash<Project *, int>::iterator end = d->m_activeBuildSteps.end();
512     if (it == end) {
513         d->m_activeBuildSteps.insert(pro, 1);
514         emit buildStateChanged(pro);
515     } else if (*it == 0) {
516         ++*it;
517         emit buildStateChanged(pro);
518     } else {
519         ++*it;
520     }
521 }
522
523 void BuildManager::decrementActiveBuildSteps(Project *pro)
524 {
525     QHash<Project *, int>::iterator it = d->m_activeBuildSteps.find(pro);
526     QHash<Project *, int>::iterator end = d->m_activeBuildSteps.end();
527     if (it == end) {
528         Q_ASSERT(false && "BuildManager d->m_activeBuildSteps says project is not building, but apparently a build step was still in the queue.");
529     } else if (*it == 1) {
530         --*it;
531         emit buildStateChanged(pro);
532     } else {
533         --*it;
534     }
535 }
536
537 } // namespace ProjectExplorer