OSDN Git Service

0a5e2db5c3dae27541a289f2fd3ea34491ab4109
[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 info@qt.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     QFutureInterface<bool> m_futureInterfaceForAysnc;
88     BuildStep *m_currentBuildStep;
89     QString m_currentConfiguration;
90     // used to decide if we are building a project to decide when to emit buildStateChanged(Project *)
91     QHash<Project *, int>  m_activeBuildSteps;
92     Project *m_previousBuildStepProject;
93     // is set to true while canceling, so that nextBuildStep knows that the BuildStep finished because of canceling
94     bool m_canceling;
95     bool m_doNotEnterEventLoop;
96     QEventLoop *m_eventLoop;
97
98     // Progress reporting to the progress manager
99     int m_progress;
100     int m_maxProgress;
101     QFutureInterface<void> *m_progressFutureInterface;
102     QFutureWatcher<void> m_progressWatcher;
103 };
104
105 BuildManagerPrivate::BuildManagerPrivate() :
106     m_running(false)
107   , m_previousBuildStepProject(0)
108   , m_canceling(false)
109   , m_doNotEnterEventLoop(false)
110   , m_eventLoop(0)
111   , m_maxProgress(0)
112   , m_progressFutureInterface(0)
113 {
114 }
115
116 BuildManager::BuildManager(ProjectExplorerPlugin *parent)
117     : QObject(parent), d(new BuildManagerPrivate)
118 {
119     ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
120     d->m_projectExplorerPlugin = parent;
121
122     connect(&d->m_watcher, SIGNAL(finished()),
123             this, SLOT(nextBuildQueue()));
124
125     connect(&d->m_watcher, SIGNAL(progressValueChanged(int)),
126             this, SLOT(progressChanged()));
127     connect(&d->m_watcher, SIGNAL(progressTextChanged(QString)),
128             this, SLOT(progressTextChanged()));
129     connect(&d->m_watcher, SIGNAL(progressRangeChanged(int, int)),
130             this, SLOT(progressChanged()));
131
132     connect(parent->session(), SIGNAL(aboutToRemoveProject(ProjectExplorer::Project *)),
133             this, SLOT(aboutToRemoveProject(ProjectExplorer::Project *)));
134
135     d->m_outputWindow = new Internal::CompileOutputWindow(this);
136     pm->addObject(d->m_outputWindow);
137
138     d->m_taskHub = pm->getObject<TaskHub>();
139     d->m_taskWindow = new Internal::TaskWindow(d->m_taskHub);
140     pm->addObject(d->m_taskWindow);
141
142     qRegisterMetaType<ProjectExplorer::BuildStep::OutputFormat>();
143     qRegisterMetaType<ProjectExplorer::BuildStep::OutputNewlineSetting>();
144
145     connect(d->m_taskWindow, SIGNAL(tasksChanged()),
146             this, SLOT(updateTaskCount()));
147
148     connect(d->m_taskWindow, SIGNAL(tasksCleared()),
149             this,SIGNAL(tasksCleared()));
150
151     connect(&d->m_progressWatcher, SIGNAL(canceled()),
152             this, SLOT(cancel()));
153     connect(&d->m_progressWatcher, SIGNAL(finished()),
154             this, SLOT(finish()));
155 }
156
157 void BuildManager::extensionsInitialized()
158 {
159     d->m_taskHub->addCategory(Constants::TASK_CATEGORY_COMPILE,
160         tr("Compile", "Category for compiler isses listened under 'Build Issues'"));
161     d->m_taskHub->addCategory(Constants::TASK_CATEGORY_BUILDSYSTEM,
162         tr("Build System", "Category for build system isses listened under 'Build Issues'"));
163 }
164
165 BuildManager::~BuildManager()
166 {
167     cancel();
168     ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
169
170     pm->removeObject(d->m_taskWindow);
171     delete d->m_taskWindow;
172
173     pm->removeObject(d->m_outputWindow);
174     delete d->m_outputWindow;
175
176     delete d;
177 }
178
179 void BuildManager::aboutToRemoveProject(ProjectExplorer::Project *p)
180 {
181     QHash<Project *, int>::iterator it = d->m_activeBuildSteps.find(p);
182     QHash<Project *, int>::iterator end = d->m_activeBuildSteps.end();
183     if (it != end && *it > 0) {
184         // We are building the project that's about to be removed.
185         // We cancel the whole queue, which isn't the nicest thing to do
186         // but a safe thing.
187         cancel();
188     }
189 }
190
191 bool BuildManager::isBuilding() const
192 {
193     // we are building even if we are not running yet
194     return !d->m_buildQueue.isEmpty() || d->m_running;
195 }
196
197 void BuildManager::cancel()
198 {
199     if (d->m_running) {
200         d->m_canceling = true;
201         d->m_watcher.cancel();
202         if (d->m_currentBuildStep->runInGuiThread()) {
203             // This is evil. A nested event loop.
204             d->m_currentBuildStep->cancel();
205             if (d->m_doNotEnterEventLoop) {
206                 d->m_doNotEnterEventLoop = false;
207             } else {
208                 d->m_eventLoop = new QEventLoop;
209                 d->m_eventLoop->exec();
210                 delete d->m_eventLoop;
211                 d->m_eventLoop = 0;
212             }
213         } else {
214             d->m_watcher.waitForFinished();
215         }
216
217         // The cancel message is added to the output window via a single shot timer
218         // since the canceling is likely to have generated new addToOutputWindow signals
219         // which are waiting in the event queue to be processed
220         // (And we want those to be before the cancel message.)
221         QTimer::singleShot(0, this, SLOT(emitCancelMessage()));
222
223         disconnectOutput(d->m_currentBuildStep);
224         decrementActiveBuildSteps(d->m_currentBuildStep->buildConfiguration()->target()->project());
225
226         d->m_progressFutureInterface->setProgressValueAndText(d->m_progress*100, tr("Build canceled")); //TODO NBS fix in qtconcurrent
227         clearBuildQueue();
228     }
229     return;
230 }
231
232 void BuildManager::updateTaskCount()
233 {
234     Core::ProgressManager *progressManager = Core::ICore::instance()->progressManager();
235     const int errors =
236             d->m_taskWindow->errorTaskCount(Constants::TASK_CATEGORY_BUILDSYSTEM)
237             + d->m_taskWindow->errorTaskCount(Constants::TASK_CATEGORY_COMPILE);
238     if (errors > 0) {
239         progressManager->setApplicationLabel(QString::number(errors));
240     } else {
241         progressManager->setApplicationLabel(QString());
242     }
243     emit tasksChanged();
244 }
245
246 void BuildManager::finish()
247 {
248     QApplication::alert(Core::ICore::instance()->mainWindow(), 3000);
249 }
250
251 void BuildManager::emitCancelMessage()
252 {
253     addToOutputWindow(tr("Canceled build."), BuildStep::ErrorMessageOutput);
254 }
255
256 void BuildManager::clearBuildQueue()
257 {
258     foreach (BuildStep *bs, d->m_buildQueue) {
259         decrementActiveBuildSteps(bs->buildConfiguration()->target()->project());
260         disconnectOutput(bs);
261     }
262
263     d->m_buildQueue.clear();
264     d->m_running = false;
265     d->m_previousBuildStepProject = 0;
266     d->m_currentBuildStep = 0;
267
268     d->m_progressFutureInterface->reportCanceled();
269     d->m_progressFutureInterface->reportFinished();
270     d->m_progressWatcher.setFuture(QFuture<void>());
271     delete d->m_progressFutureInterface;
272     d->m_progressFutureInterface = 0;
273     d->m_maxProgress = 0;
274
275     emit buildQueueFinished(false);
276 }
277
278
279 void BuildManager::toggleOutputWindow()
280 {
281     d->m_outputWindow->toggle(false);
282 }
283
284 void BuildManager::showTaskWindow()
285 {
286     d->m_taskWindow->popup(false);
287 }
288
289 void BuildManager::toggleTaskWindow()
290 {
291     d->m_taskWindow->toggle(false);
292 }
293
294 bool BuildManager::tasksAvailable() const
295 {
296     const int count =
297             d->m_taskWindow->taskCount(Constants::TASK_CATEGORY_BUILDSYSTEM)
298             + d->m_taskWindow->taskCount(Constants::TASK_CATEGORY_COMPILE);
299     return count > 0;
300 }
301
302 void BuildManager::startBuildQueue()
303 {
304     if (d->m_buildQueue.isEmpty()) {
305         emit buildQueueFinished(true);
306         return;
307     }
308     if (!d->m_running) {
309         // Progress Reporting
310         Core::ProgressManager *progressManager = Core::ICore::instance()->progressManager();
311         d->m_progressFutureInterface = new QFutureInterface<void>;
312         d->m_progressWatcher.setFuture(d->m_progressFutureInterface->future());
313         d->m_outputWindow->clearContents();
314         d->m_taskHub->clearTasks(Constants::TASK_CATEGORY_COMPILE);
315         d->m_taskHub->clearTasks(Constants::TASK_CATEGORY_BUILDSYSTEM);
316         progressManager->setApplicationLabel(QString());
317         Core::FutureProgress *progress = progressManager->addTask(d->m_progressFutureInterface->future(),
318               tr("Build"),
319               Constants::TASK_BUILD,
320               Core::ProgressManager::KeepOnFinish | Core::ProgressManager::ShowInApplicationIcon);
321         connect(progress, SIGNAL(clicked()), this, SLOT(showBuildResults()));
322         progress->setWidget(new Internal::BuildProgress(d->m_taskWindow));
323         d->m_progress = 0;
324         d->m_progressFutureInterface->setProgressRange(0, d->m_maxProgress * 100);
325
326         d->m_running = true;
327         d->m_canceling = false;
328         d->m_progressFutureInterface->reportStarted();
329         nextStep();
330     } else {
331         // Already running
332         d->m_progressFutureInterface->setProgressRange(0, d->m_maxProgress * 100);
333         d->m_progressFutureInterface->setProgressValueAndText(d->m_progress*100, msgProgress(d->m_progress, d->m_maxProgress));
334     }
335 }
336
337 void BuildManager::showBuildResults()
338 {
339     if (tasksAvailable())
340         toggleTaskWindow();
341     else
342         toggleOutputWindow();
343     //toggleTaskWindow();
344 }
345
346 void BuildManager::addToTaskWindow(const ProjectExplorer::Task &task)
347 {
348     d->m_outputWindow->registerPositionOf(task);
349     // Distribute to all others
350     d->m_taskHub->addTask(task);
351 }
352
353 void BuildManager::addToOutputWindow(const QString &string, BuildStep::OutputFormat format,
354     BuildStep::OutputNewlineSetting newLineSetting)
355 {
356     QString stringToWrite = string;
357     if (newLineSetting == BuildStep::DoAppendNewline)
358         stringToWrite += QLatin1Char('\n');
359     d->m_outputWindow->appendText(stringToWrite, format);
360 }
361
362 void BuildManager::buildStepFinishedAsync()
363 {
364     disconnect(d->m_currentBuildStep, SIGNAL(finished()),
365                this, SLOT(buildStepFinishedAsync()));
366     d->m_futureInterfaceForAysnc = QFutureInterface<bool>();
367     if (d->m_canceling) {
368         if (d->m_eventLoop)
369             d->m_eventLoop->exit();
370         else
371             d->m_doNotEnterEventLoop = true;
372     } else {
373         nextBuildQueue();
374     }
375 }
376
377 void BuildManager::nextBuildQueue()
378 {
379     if (d->m_canceling)
380         return;
381
382     disconnectOutput(d->m_currentBuildStep);
383     ++d->m_progress;
384     d->m_progressFutureInterface->setProgressValueAndText(d->m_progress*100, msgProgress(d->m_progress, d->m_maxProgress));
385     decrementActiveBuildSteps(d->m_currentBuildStep->buildConfiguration()->target()->project());
386
387     bool result = d->m_watcher.result();
388     if (!result) {
389         // Build Failure
390         const QString projectName = d->m_currentBuildStep->buildConfiguration()->target()->project()->displayName();
391         const QString targetName = d->m_currentBuildStep->buildConfiguration()->target()->displayName();
392         addToOutputWindow(tr("Error while building project %1 (target: %2)").arg(projectName, targetName), BuildStep::ErrorOutput);
393         addToOutputWindow(tr("When executing build step '%1'").arg(d->m_currentBuildStep->displayName()), BuildStep::ErrorOutput);
394         // NBS TODO fix in qtconcurrent
395         d->m_progressFutureInterface->setProgressValueAndText(d->m_progress*100, tr("Error while building project %1 (target: %2)").arg(projectName, targetName));
396     }
397
398     if (result)
399         nextStep();
400     else
401         clearBuildQueue();
402 }
403
404 void BuildManager::progressChanged()
405 {
406     if (!d->m_progressFutureInterface)
407         return;
408     int range = d->m_watcher.progressMaximum() - d->m_watcher.progressMinimum();
409     if (range != 0) {
410         int percent = (d->m_watcher.progressValue() - d->m_watcher.progressMinimum()) * 100 / range;
411         d->m_progressFutureInterface->setProgressValueAndText(d->m_progress * 100 + percent, msgProgress(d->m_progress, d->m_maxProgress)
412                                                               + QLatin1Char('\n') + d->m_watcher.progressText());
413     }
414 }
415
416 void BuildManager::progressTextChanged()
417 {
418     int range = d->m_watcher.progressMaximum() - d->m_watcher.progressMinimum();
419     int percent = 0;
420     if (range != 0)
421         percent = (d->m_watcher.progressValue() - d->m_watcher.progressMinimum()) * 100 / range;
422     d->m_progressFutureInterface->setProgressValueAndText(d->m_progress*100 + percent, msgProgress(d->m_progress, d->m_maxProgress) +
423                                                           QLatin1Char('\n') + d->m_watcher.progressText());
424 }
425
426 void BuildManager::nextStep()
427 {
428     if (!d->m_buildQueue.empty()) {
429         d->m_currentBuildStep = d->m_buildQueue.front();
430         d->m_buildQueue.pop_front();
431
432         if (d->m_currentBuildStep->buildConfiguration()->target()->project() != d->m_previousBuildStepProject) {
433             const QString projectName = d->m_currentBuildStep->buildConfiguration()->target()->project()->displayName();
434             addToOutputWindow(tr("Running build steps for project %1...")
435                               .arg(projectName), BuildStep::MessageOutput);
436             d->m_previousBuildStepProject = d->m_currentBuildStep->buildConfiguration()->target()->project();
437         }
438         if (d->m_currentBuildStep->runInGuiThread()) {
439             connect (d->m_currentBuildStep, SIGNAL(finished()),
440                      this, SLOT(buildStepFinishedAsync()));
441             d->m_watcher.setFuture(d->m_futureInterfaceForAysnc.future());
442             d->m_currentBuildStep->run(d->m_futureInterfaceForAysnc);
443         } else {
444             d->m_watcher.setFuture(QtConcurrent::run(&BuildStep::run, d->m_currentBuildStep));
445         }
446     } else {
447         d->m_running = false;
448         d->m_previousBuildStepProject = 0;
449         d->m_progressFutureInterface->reportFinished();
450         d->m_progressWatcher.setFuture(QFuture<void>());
451         d->m_currentBuildStep = 0;
452         delete d->m_progressFutureInterface;
453         d->m_progressFutureInterface = 0;
454         d->m_maxProgress = 0;
455         emit buildQueueFinished(true);
456     }
457 }
458
459 bool BuildManager::buildQueueAppend(QList<BuildStep *> steps)
460 {
461     int count = steps.size();
462     bool init = true;
463     int i = 0;
464     for (; i < count; ++i) {
465         BuildStep *bs = steps.at(i);
466         connect(bs, SIGNAL(addTask(ProjectExplorer::Task)),
467                 this, SLOT(addToTaskWindow(ProjectExplorer::Task)));
468         connect(bs, SIGNAL(addOutput(QString, ProjectExplorer::BuildStep::OutputFormat, ProjectExplorer::BuildStep::OutputNewlineSetting)),
469                 this, SLOT(addToOutputWindow(QString, ProjectExplorer::BuildStep::OutputFormat, ProjectExplorer::BuildStep::OutputNewlineSetting)));
470         init = bs->init();
471         if (!init)
472             break;
473     }
474     if (!init) {
475         BuildStep *bs = steps.at(i);
476
477         // cleaning up
478         // print something for the user
479         const QString projectName = bs->buildConfiguration()->target()->project()->displayName();
480         const QString targetName = bs->buildConfiguration()->target()->displayName();
481         addToOutputWindow(tr("Error while building project %1 (target: %2)").arg(projectName, targetName), BuildStep::ErrorOutput);
482         addToOutputWindow(tr("When executing build step '%1'").arg(bs->displayName()), BuildStep::ErrorOutput);
483
484         // disconnect the buildsteps again
485         for (int j = 0; j <= i; ++j)
486             disconnectOutput(steps.at(j));
487         return false;
488     }
489
490     // Everthing init() well
491     for (i = 0; i < count; ++i) {
492         ++d->m_maxProgress;
493         d->m_buildQueue.append(steps.at(i));
494         incrementActiveBuildSteps(steps.at(i)->buildConfiguration()->target()->project());
495     }
496     return true;
497 }
498
499 bool BuildManager::buildList(BuildStepList *bsl)
500 {
501     return buildLists(QList<BuildStepList *>() << bsl);
502 }
503
504 bool BuildManager::buildLists(QList<BuildStepList *> bsls)
505 {
506     QList<BuildStep *> steps;
507     foreach(BuildStepList *list, bsls)
508         steps.append(list->steps());
509
510     bool success = buildQueueAppend(steps);
511     if (!success) {
512         d->m_outputWindow->popup(false);
513         return false;
514     }
515
516     if (ProjectExplorerPlugin::instance()->projectExplorerSettings().showCompilerOutput)
517         d->m_outputWindow->popup(false);
518     startBuildQueue();
519     return true;
520 }
521
522 void BuildManager::appendStep(BuildStep *step)
523 {
524     bool success = buildQueueAppend(QList<BuildStep *>() << step);
525     if (!success) {
526         d->m_outputWindow->popup(false);
527         return;
528     }
529     if (ProjectExplorerPlugin::instance()->projectExplorerSettings().showCompilerOutput)
530         d->m_outputWindow->popup(false);
531     startBuildQueue();
532 }
533
534 bool BuildManager::isBuilding(Project *pro)
535 {
536     QHash<Project *, int>::iterator it = d->m_activeBuildSteps.find(pro);
537     QHash<Project *, int>::iterator end = d->m_activeBuildSteps.end();
538     if (it == end || *it == 0)
539         return false;
540     else
541         return true;
542 }
543
544 bool BuildManager::isBuilding(BuildStep *step)
545 {
546     return (d->m_currentBuildStep == step) || d->m_buildQueue.contains(step);
547 }
548
549 void BuildManager::incrementActiveBuildSteps(Project *pro)
550 {
551     QHash<Project *, int>::iterator it = d->m_activeBuildSteps.find(pro);
552     QHash<Project *, int>::iterator end = d->m_activeBuildSteps.end();
553     if (it == end) {
554         d->m_activeBuildSteps.insert(pro, 1);
555         emit buildStateChanged(pro);
556     } else if (*it == 0) {
557         ++*it;
558         emit buildStateChanged(pro);
559     } else {
560         ++*it;
561     }
562 }
563
564 void BuildManager::decrementActiveBuildSteps(Project *pro)
565 {
566     QHash<Project *, int>::iterator it = d->m_activeBuildSteps.find(pro);
567     QHash<Project *, int>::iterator end = d->m_activeBuildSteps.end();
568     if (it == end) {
569         Q_ASSERT(false && "BuildManager d->m_activeBuildSteps says project is not building, but apparently a build step was still in the queue.");
570     } else if (*it == 1) {
571         --*it;
572         emit buildStateChanged(pro);
573     } else {
574         --*it;
575     }
576 }
577
578 void BuildManager::disconnectOutput(BuildStep *bs)
579 {
580     disconnect(bs, SIGNAL(addTask(ProjectExplorer::Task)),
581                this, SLOT(addToTaskWindow(ProjectExplorer::Task)));
582     disconnect(bs, SIGNAL(addOutput(QString, ProjectExplorer::BuildStep::OutputFormat,
583         ProjectExplorer::BuildStep::OutputNewlineSetting)),
584         this, SLOT(addToOutputWindow(QString, ProjectExplorer::BuildStep::OutputFormat,
585             ProjectExplorer::BuildStep::OutputNewlineSetting)));
586 }
587
588 } // namespace ProjectExplorer