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