1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25
26 #include "buildstep.h"
27
28 #include "buildconfiguration.h"
29 #include "buildsteplist.h"
30 #include "customparser.h"
31 #include "deployconfiguration.h"
32 #include "kitinformation.h"
33 #include "project.h"
34 #include "projectexplorer.h"
35 #include "projectexplorerconstants.h"
36 #include "target.h"
37
38 #include <utils/algorithm.h>
39 #include <utils/fileinprojectfinder.h>
40 #include <utils/layoutbuilder.h>
41 #include <utils/outputformatter.h>
42 #include <utils/qtcassert.h>
43 #include <utils/runextensions.h>
44 #include <utils/variablechooser.h>
45
46 #include <QFormLayout>
47 #include <QFutureWatcher>
48 #include <QPointer>
49
50 /*!
51 \class ProjectExplorer::BuildStep
52
53 \brief The BuildStep class provides build steps for projects.
54
55 Build steps are the primary way plugin developers can customize
56 how their projects (or projects from other plugins) are built.
57
58 Projects are built by taking the list of build steps
59 from the project and calling first \c init() and then \c run() on them.
60
61 To change the way your project is built, reimplement
62 this class and add your build step to the build step list of the project.
63
64 \note The projects own the build step. Do not delete them yourself.
65
66 \c init() is called in the GUI thread and can be used to query the
67 project for any information you need.
68
69 \c run() is run via Utils::runAsync in a separate thread. If you need an
70 event loop, you need to create it yourself.
71 */
72
73 /*!
74 \fn bool ProjectExplorer::BuildStep::init()
75
76 This function is run in the GUI thread. Use it to retrieve any information
77 that you need in the run() function.
78 */
79
80 /*!
81 \fn void ProjectExplorer::BuildStep::run(QFutureInterface<bool> &fi)
82
83 Reimplement this function. It is called when the target is built.
84 By default, this function is NOT run in the GUI thread, but runs in its
85 own thread. If you need an event loop, you need to create one.
86 This function should block until the task is done
87
88 The absolute minimal implementation is:
89 \code
90 fi.reportResult(true);
91 \endcode
92
93 By returning \c true from runInGuiThread(), this function is called in
94 the GUI thread. Then the function should not block and instead the
95 finished() signal should be emitted.
96
97 \sa runInGuiThread()
98 */
99
100 /*!
101 \fn BuildStepConfigWidget *ProjectExplorer::BuildStep::createConfigWidget()
102
103 Returns the Widget shown in the target settings dialog for this build step.
104 Ownership is transferred to the caller.
105 */
106
107 /*!
108 \fn void ProjectExplorer::BuildStep::addTask(const ProjectExplorer::Task &task)
109 Adds \a task.
110 */
111
112 /*!
113 \fn void ProjectExplorer::BuildStep::addOutput(const QString &string, ProjectExplorer::BuildStep::OutputFormat format,
114 ProjectExplorer::BuildStep::OutputNewlineSetting newlineSetting = DoAppendNewline) const
115
116 The \a string is added to the generated output, usually in the output pane.
117 It should be in plain text, with the format in the parameter.
118 */
119
120 /*!
121 \fn void ProjectExplorer::BuildStep::finished()
122 This signal needs to be emitted if the build step runs in the GUI thread.
123 */
124
125 using namespace Utils;
126
127 static const char buildStepEnabledKey[] = "ProjectExplorer.BuildStep.Enabled";
128
129 namespace ProjectExplorer {
130
131 static QList<BuildStepFactory *> g_buildStepFactories;
132
BuildStep(BuildStepList * bsl,Utils::Id id)133 BuildStep::BuildStep(BuildStepList *bsl, Utils::Id id) :
134 ProjectConfiguration(bsl, id)
135 {
136 QTC_CHECK(bsl->target() && bsl->target() == this->target());
137 connect(this, &ProjectConfiguration::displayNameChanged,
138 this, &BuildStep::updateSummary);
139 // m_displayName = step->displayName();
140 // m_summaryText = "<b>" + m_displayName + "</b>";
141 }
142
~BuildStep()143 BuildStep::~BuildStep()
144 {
145 emit finished(false);
146 }
147
run()148 void BuildStep::run()
149 {
150 m_cancelFlag = false;
151 doRun();
152 }
153
cancel()154 void BuildStep::cancel()
155 {
156 m_cancelFlag = true;
157 doCancel();
158 }
159
doCreateConfigWidget()160 QWidget *BuildStep::doCreateConfigWidget()
161 {
162 QWidget *widget = createConfigWidget();
163
164 const auto recreateSummary = [this] {
165 if (m_summaryUpdater)
166 setSummaryText(m_summaryUpdater());
167 };
168
169 for (BaseAspect *aspect : qAsConst(m_aspects))
170 connect(aspect, &BaseAspect::changed, widget, recreateSummary);
171
172 connect(buildConfiguration(), &BuildConfiguration::buildDirectoryChanged,
173 widget, recreateSummary);
174
175 recreateSummary();
176
177 return widget;
178 }
179
createConfigWidget()180 QWidget *BuildStep::createConfigWidget()
181 {
182 Layouting::Form builder;
183 for (BaseAspect *aspect : qAsConst(m_aspects)) {
184 if (aspect->isVisible())
185 aspect->addToLayout(builder.finishRow());
186 }
187 auto widget = builder.emerge(false);
188
189 if (m_addMacroExpander)
190 VariableChooser::addSupportForChildWidgets(widget, macroExpander());
191
192 return widget;
193 }
194
fromMap(const QVariantMap & map)195 bool BuildStep::fromMap(const QVariantMap &map)
196 {
197 m_enabled = map.value(buildStepEnabledKey, true).toBool();
198 return ProjectConfiguration::fromMap(map);
199 }
200
toMap() const201 QVariantMap BuildStep::toMap() const
202 {
203 QVariantMap map = ProjectConfiguration::toMap();
204 map.insert(buildStepEnabledKey, m_enabled);
205 return map;
206 }
207
buildConfiguration() const208 BuildConfiguration *BuildStep::buildConfiguration() const
209 {
210 auto config = qobject_cast<BuildConfiguration *>(parent()->parent());
211 if (config)
212 return config;
213
214 // step is not part of a build configuration, use active build configuration of step's target
215 return target()->activeBuildConfiguration();
216 }
217
deployConfiguration() const218 DeployConfiguration *BuildStep::deployConfiguration() const
219 {
220 auto config = qobject_cast<DeployConfiguration *>(parent()->parent());
221 if (config)
222 return config;
223 // See comment in buildConfiguration()
224 QTC_CHECK(false);
225 // step is not part of a deploy configuration, use active deploy configuration of step's target
226 return target()->activeDeployConfiguration();
227 }
228
projectConfiguration() const229 ProjectConfiguration *BuildStep::projectConfiguration() const
230 {
231 return static_cast<ProjectConfiguration *>(parent()->parent());
232 }
233
buildSystem() const234 BuildSystem *BuildStep::buildSystem() const
235 {
236 if (auto bc = buildConfiguration())
237 return bc->buildSystem();
238 return target()->buildSystem();
239 }
240
buildEnvironment() const241 Environment BuildStep::buildEnvironment() const
242 {
243 if (const auto bc = qobject_cast<BuildConfiguration *>(parent()->parent()))
244 return bc->environment();
245 if (const auto bc = target()->activeBuildConfiguration())
246 return bc->environment();
247 return Environment::systemEnvironment();
248 }
249
buildDirectory() const250 FilePath BuildStep::buildDirectory() const
251 {
252 if (auto bc = buildConfiguration())
253 return bc->buildDirectory();
254 return {};
255 }
256
buildType() const257 BuildConfiguration::BuildType BuildStep::buildType() const
258 {
259 if (auto bc = buildConfiguration())
260 return bc->buildType();
261 return BuildConfiguration::Unknown;
262 }
263
macroExpander() const264 Utils::MacroExpander *BuildStep::macroExpander() const
265 {
266 if (auto bc = buildConfiguration())
267 return bc->macroExpander();
268 return Utils::globalMacroExpander();
269 }
270
fallbackWorkingDirectory() const271 QString BuildStep::fallbackWorkingDirectory() const
272 {
273 if (buildConfiguration())
274 return {Constants::DEFAULT_WORKING_DIR};
275 return {Constants::DEFAULT_WORKING_DIR_ALTERNATE};
276 }
277
setupOutputFormatter(OutputFormatter * formatter)278 void BuildStep::setupOutputFormatter(OutputFormatter *formatter)
279 {
280 if (qobject_cast<BuildConfiguration *>(parent()->parent())) {
281 for (const Utils::Id id : buildConfiguration()->customParsers()) {
282 if (Internal::CustomParser * const parser = Internal::CustomParser::createFromId(id))
283 formatter->addLineParser(parser);
284 }
285 }
286 Utils::FileInProjectFinder fileFinder;
287 fileFinder.setProjectDirectory(project()->projectDirectory());
288 fileFinder.setProjectFiles(project()->files(Project::AllFiles));
289 formatter->setFileFinder(fileFinder);
290 }
291
reportRunResult(QFutureInterface<bool> & fi,bool success)292 void BuildStep::reportRunResult(QFutureInterface<bool> &fi, bool success)
293 {
294 fi.reportResult(success);
295 fi.reportFinished();
296 }
297
widgetExpandedByDefault() const298 bool BuildStep::widgetExpandedByDefault() const
299 {
300 return m_widgetExpandedByDefault;
301 }
302
setWidgetExpandedByDefault(bool widgetExpandedByDefault)303 void BuildStep::setWidgetExpandedByDefault(bool widgetExpandedByDefault)
304 {
305 m_widgetExpandedByDefault = widgetExpandedByDefault;
306 }
307
data(Utils::Id id) const308 QVariant BuildStep::data(Utils::Id id) const
309 {
310 Q_UNUSED(id)
311 return {};
312 }
313
314 /*!
315 \fn BuildStep::isImmutable()
316
317 If this function returns \c true, the user cannot delete this build step for
318 this target and the user is prevented from changing the order in which
319 immutable steps are run. The default implementation returns \c false.
320 */
321
runInThread(const std::function<bool ()> & syncImpl)322 void BuildStep::runInThread(const std::function<bool()> &syncImpl)
323 {
324 m_runInGuiThread = false;
325 m_cancelFlag = false;
326 auto * const watcher = new QFutureWatcher<bool>(this);
327 connect(watcher, &QFutureWatcher<bool>::finished, this, [this, watcher] {
328 emit finished(watcher->result());
329 watcher->deleteLater();
330 });
331 watcher->setFuture(Utils::runAsync(syncImpl));
332 }
333
cancelChecker() const334 std::function<bool ()> BuildStep::cancelChecker() const
335 {
336 return [step = QPointer<const BuildStep>(this)] { return step && step->isCanceled(); };
337 }
338
isCanceled() const339 bool BuildStep::isCanceled() const
340 {
341 return m_cancelFlag;
342 }
343
doCancel()344 void BuildStep::doCancel()
345 {
346 QTC_ASSERT(!m_runInGuiThread, qWarning() << "Build step" << displayName()
347 << "neeeds to implement the doCancel() function");
348 }
349
addMacroExpander()350 void BuildStep::addMacroExpander()
351 {
352 m_addMacroExpander = true;
353 }
354
setEnabled(bool b)355 void BuildStep::setEnabled(bool b)
356 {
357 if (m_enabled == b)
358 return;
359 m_enabled = b;
360 emit enabledChanged();
361 }
362
stepList() const363 BuildStepList *BuildStep::stepList() const
364 {
365 return qobject_cast<BuildStepList *>(parent());
366 }
367
enabled() const368 bool BuildStep::enabled() const
369 {
370 return m_enabled;
371 }
372
BuildStepFactory()373 BuildStepFactory::BuildStepFactory()
374 {
375 g_buildStepFactories.append(this);
376 }
377
~BuildStepFactory()378 BuildStepFactory::~BuildStepFactory()
379 {
380 g_buildStepFactories.removeOne(this);
381 }
382
allBuildStepFactories()383 const QList<BuildStepFactory *> BuildStepFactory::allBuildStepFactories()
384 {
385 return g_buildStepFactories;
386 }
387
canHandle(BuildStepList * bsl) const388 bool BuildStepFactory::canHandle(BuildStepList *bsl) const
389 {
390 if (!m_supportedStepLists.isEmpty() && !m_supportedStepLists.contains(bsl->id()))
391 return false;
392
393 auto config = qobject_cast<ProjectConfiguration *>(bsl->parent());
394
395 if (!m_supportedDeviceTypes.isEmpty()) {
396 Target *target = bsl->target();
397 QTC_ASSERT(target, return false);
398 Utils::Id deviceType = DeviceTypeKitAspect::deviceTypeId(target->kit());
399 if (!m_supportedDeviceTypes.contains(deviceType))
400 return false;
401 }
402
403 if (m_supportedProjectType.isValid()) {
404 if (!config)
405 return false;
406 Utils::Id projectId = config->project()->id();
407 if (projectId != m_supportedProjectType)
408 return false;
409 }
410
411 if (!m_isRepeatable && bsl->contains(m_info.id))
412 return false;
413
414 if (m_supportedConfiguration.isValid()) {
415 if (!config)
416 return false;
417 Utils::Id configId = config->id();
418 if (configId != m_supportedConfiguration)
419 return false;
420 }
421
422 return true;
423 }
424
setDisplayName(const QString & displayName)425 void BuildStepFactory::setDisplayName(const QString &displayName)
426 {
427 m_info.displayName = displayName;
428 }
429
setFlags(BuildStepInfo::Flags flags)430 void BuildStepFactory::setFlags(BuildStepInfo::Flags flags)
431 {
432 m_info.flags = flags;
433 }
434
setSupportedStepList(Utils::Id id)435 void BuildStepFactory::setSupportedStepList(Utils::Id id)
436 {
437 m_supportedStepLists = {id};
438 }
439
setSupportedStepLists(const QList<Utils::Id> & ids)440 void BuildStepFactory::setSupportedStepLists(const QList<Utils::Id> &ids)
441 {
442 m_supportedStepLists = ids;
443 }
444
setSupportedConfiguration(Utils::Id id)445 void BuildStepFactory::setSupportedConfiguration(Utils::Id id)
446 {
447 m_supportedConfiguration = id;
448 }
449
setSupportedProjectType(Utils::Id id)450 void BuildStepFactory::setSupportedProjectType(Utils::Id id)
451 {
452 m_supportedProjectType = id;
453 }
454
setSupportedDeviceType(Utils::Id id)455 void BuildStepFactory::setSupportedDeviceType(Utils::Id id)
456 {
457 m_supportedDeviceTypes = {id};
458 }
459
setSupportedDeviceTypes(const QList<Utils::Id> & ids)460 void BuildStepFactory::setSupportedDeviceTypes(const QList<Utils::Id> &ids)
461 {
462 m_supportedDeviceTypes = ids;
463 }
464
stepInfo() const465 BuildStepInfo BuildStepFactory::stepInfo() const
466 {
467 return m_info;
468 }
469
stepId() const470 Utils::Id BuildStepFactory::stepId() const
471 {
472 return m_info.id;
473 }
474
create(BuildStepList * parent)475 BuildStep *BuildStepFactory::create(BuildStepList *parent)
476 {
477 BuildStep *step = m_info.creator(parent);
478 step->setDefaultDisplayName(m_info.displayName);
479 return step;
480 }
481
restore(BuildStepList * parent,const QVariantMap & map)482 BuildStep *BuildStepFactory::restore(BuildStepList *parent, const QVariantMap &map)
483 {
484 BuildStep *bs = create(parent);
485 if (!bs)
486 return nullptr;
487 if (!bs->fromMap(map)) {
488 QTC_CHECK(false);
489 delete bs;
490 return nullptr;
491 }
492 return bs;
493 }
494
summaryText() const495 QString BuildStep::summaryText() const
496 {
497 if (m_summaryText.isEmpty())
498 return QString("<b>%1</b>").arg(displayName());
499
500 return m_summaryText;
501 }
502
setSummaryText(const QString & summaryText)503 void BuildStep::setSummaryText(const QString &summaryText)
504 {
505 if (summaryText != m_summaryText) {
506 m_summaryText = summaryText;
507 emit updateSummary();
508 }
509 }
510
setSummaryUpdater(const std::function<QString ()> & summaryUpdater)511 void BuildStep::setSummaryUpdater(const std::function<QString()> &summaryUpdater)
512 {
513 m_summaryUpdater = summaryUpdater;
514 }
515
516 } // ProjectExplorer
517