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 "kitinformation.h"
27 
28 #include "abi.h"
29 #include "devicesupport/desktopdevice.h"
30 #include "devicesupport/devicemanager.h"
31 #include "devicesupport/devicemanagermodel.h"
32 #include "devicesupport/idevicefactory.h"
33 #include "projectexplorerconstants.h"
34 #include "kit.h"
35 #include "toolchain.h"
36 #include "toolchainmanager.h"
37 
38 #include <docker/dockerconstants.h>
39 
40 #include <ssh/sshconnection.h>
41 
42 #include <utils/algorithm.h>
43 #include <utils/elidinglabel.h>
44 #include <utils/environment.h>
45 #include <utils/environmentdialog.h>
46 #include <utils/layoutbuilder.h>
47 #include <utils/macroexpander.h>
48 #include <utils/pathchooser.h>
49 #include <utils/qtcassert.h>
50 #include <utils/variablechooser.h>
51 
52 #include <QCheckBox>
53 #include <QComboBox>
54 #include <QDir>
55 #include <QFileInfo>
56 #include <QFontMetrics>
57 #include <QGridLayout>
58 #include <QLabel>
59 #include <QPushButton>
60 #include <QVBoxLayout>
61 
62 using namespace Utils;
63 
64 namespace ProjectExplorer {
65 
66 const char KITINFORMATION_ID_V1[] = "PE.Profile.ToolChain";
67 const char KITINFORMATION_ID_V2[] = "PE.Profile.ToolChains";
68 const char KITINFORMATION_ID_V3[] = "PE.Profile.ToolChainsV3";
69 
70 // --------------------------------------------------------------------------
71 // SysRootKitAspect:
72 // --------------------------------------------------------------------------
73 
74 namespace Internal {
75 class SysRootKitAspectWidget : public KitAspectWidget
76 {
77     Q_DECLARE_TR_FUNCTIONS(ProjectExplorer::SysRootKitAspect)
78 
79 public:
SysRootKitAspectWidget(Kit * k,const KitAspect * ki)80     SysRootKitAspectWidget(Kit *k, const KitAspect *ki) : KitAspectWidget(k, ki)
81     {
82         m_chooser = createSubWidget<PathChooser>();
83         m_chooser->setExpectedKind(Utils::PathChooser::ExistingDirectory);
84         m_chooser->setHistoryCompleter(QLatin1String("PE.SysRoot.History"));
85         m_chooser->setFilePath(SysRootKitAspect::sysRoot(k));
86         connect(m_chooser, &Utils::PathChooser::pathChanged,
87                 this, &SysRootKitAspectWidget::pathWasChanged);
88     }
89 
~SysRootKitAspectWidget()90     ~SysRootKitAspectWidget() override { delete m_chooser; }
91 
92 private:
makeReadOnly()93     void makeReadOnly() override { m_chooser->setReadOnly(true); }
94 
addToLayout(LayoutBuilder & builder)95     void addToLayout(LayoutBuilder &builder) override
96     {
97         addMutableAction(m_chooser);
98         builder.addItem(Layouting::Span(2, m_chooser));
99     }
100 
refresh()101     void refresh() override
102     {
103         if (!m_ignoreChange)
104             m_chooser->setFilePath(SysRootKitAspect::sysRoot(m_kit));
105     }
106 
pathWasChanged()107     void pathWasChanged()
108     {
109         m_ignoreChange = true;
110         SysRootKitAspect::setSysRoot(m_kit, m_chooser->filePath());
111         m_ignoreChange = false;
112     }
113 
114     Utils::PathChooser *m_chooser;
115     bool m_ignoreChange = false;
116 };
117 } // namespace Internal
118 
SysRootKitAspect()119 SysRootKitAspect::SysRootKitAspect()
120 {
121     setObjectName(QLatin1String("SysRootInformation"));
122     setId(SysRootKitAspect::id());
123     setDisplayName(tr("Sysroot"));
124     setDescription(tr("The root directory of the system image to use.<br>"
125                       "Leave empty when building for the desktop."));
126     setPriority(31000);
127 }
128 
validate(const Kit * k) const129 Tasks SysRootKitAspect::validate(const Kit *k) const
130 {
131     Tasks result;
132     const Utils::FilePath dir = SysRootKitAspect::sysRoot(k);
133     if (dir.isEmpty())
134         return result;
135 
136     if (dir.toString().startsWith("target:") || dir.toString().startsWith("remote:"))
137         return result;
138 
139     const QFileInfo fi = dir.toFileInfo();
140 
141     if (!fi.exists()) {
142         result << BuildSystemTask(Task::Warning,
143                     tr("Sys Root \"%1\" does not exist in the file system.").arg(dir.toUserOutput()));
144     } else if (!fi.isDir()) {
145         result << BuildSystemTask(Task::Warning,
146                     tr("Sys Root \"%1\" is not a directory.").arg(dir.toUserOutput()));
147     } else if (QDir(dir.toString()).entryList(QDir::AllEntries | QDir::NoDotAndDotDot).isEmpty()) {
148         result << BuildSystemTask(Task::Warning,
149                     tr("Sys Root \"%1\" is empty.").arg(dir.toUserOutput()));
150     }
151     return result;
152 }
153 
createConfigWidget(Kit * k) const154 KitAspectWidget *SysRootKitAspect::createConfigWidget(Kit *k) const
155 {
156     QTC_ASSERT(k, return nullptr);
157 
158     return new Internal::SysRootKitAspectWidget(k, this);
159 }
160 
toUserOutput(const Kit * k) const161 KitAspect::ItemList SysRootKitAspect::toUserOutput(const Kit *k) const
162 {
163     return {{tr("Sys Root"), sysRoot(k).toUserOutput()}};
164 }
165 
addToMacroExpander(Kit * kit,Utils::MacroExpander * expander) const166 void SysRootKitAspect::addToMacroExpander(Kit *kit, Utils::MacroExpander *expander) const
167 {
168     QTC_ASSERT(kit, return);
169 
170     expander->registerFileVariables("SysRoot", tr("Sys Root"), [kit] {
171         return SysRootKitAspect::sysRoot(kit);
172     });
173 }
174 
id()175 Utils::Id SysRootKitAspect::id()
176 {
177     return "PE.Profile.SysRoot";
178 }
179 
sysRoot(const Kit * k)180 Utils::FilePath SysRootKitAspect::sysRoot(const Kit *k)
181 {
182     if (!k)
183         return Utils::FilePath();
184 
185     if (!k->value(SysRootKitAspect::id()).toString().isEmpty())
186         return Utils::FilePath::fromString(k->value(SysRootKitAspect::id()).toString());
187 
188     for (ToolChain *tc : ToolChainKitAspect::toolChains(k)) {
189         if (!tc->sysRoot().isEmpty())
190             return Utils::FilePath::fromString(tc->sysRoot());
191     }
192 
193     return Utils::FilePath();
194 }
195 
setSysRoot(Kit * k,const Utils::FilePath & v)196 void SysRootKitAspect::setSysRoot(Kit *k, const Utils::FilePath &v)
197 {
198     if (!k)
199         return;
200 
201     for (ToolChain *tc : ToolChainKitAspect::toolChains(k)) {
202         if (!tc->sysRoot().isEmpty()) {
203             // It's the sysroot from toolchain, don't set it.
204             if (tc->sysRoot() == v.toString())
205                 return;
206 
207             // We've changed the default toolchain sysroot, set it.
208             break;
209         }
210     }
211     k->setValue(SysRootKitAspect::id(), v.toString());
212 }
213 
214 // --------------------------------------------------------------------------
215 // ToolChainKitAspect:
216 // --------------------------------------------------------------------------
217 
218 namespace Internal {
219 class ToolChainKitAspectWidget final : public KitAspectWidget
220 {
221     Q_DECLARE_TR_FUNCTIONS(ProjectExplorer::ToolChainKitAspect)
222 
223 public:
ToolChainKitAspectWidget(Kit * k,const KitAspect * ki)224     ToolChainKitAspectWidget(Kit *k, const KitAspect *ki) : KitAspectWidget(k, ki)
225     {
226         m_mainWidget = createSubWidget<QWidget>();
227         m_mainWidget->setContentsMargins(0, 0, 0, 0);
228 
229         auto layout = new QGridLayout(m_mainWidget);
230         layout->setContentsMargins(0, 0, 0, 0);
231         layout->setColumnStretch(1, 2);
232 
233         QList<Utils::Id> languageList = ToolChainManager::allLanguages();
234         Utils::sort(languageList, [](Utils::Id l1, Utils::Id l2) {
235             return ToolChainManager::displayNameOfLanguageId(l1)
236                     < ToolChainManager::displayNameOfLanguageId(l2);
237         });
238         QTC_ASSERT(!languageList.isEmpty(), return);
239         int row = 0;
240         for (Utils::Id l : qAsConst(languageList)) {
241             layout->addWidget(new QLabel(ToolChainManager::displayNameOfLanguageId(l) + ':'), row, 0);
242             auto cb = new QComboBox;
243             cb->setSizePolicy(QSizePolicy::Ignored, cb->sizePolicy().verticalPolicy());
244             cb->setToolTip(ki->description());
245 
246             m_languageComboboxMap.insert(l, cb);
247             layout->addWidget(cb, row, 1);
248             ++row;
249 
250             connect(cb, QOverload<int>::of(&QComboBox::currentIndexChanged),
251                     this, [this, l](int idx) { currentToolChainChanged(l, idx); });
252         }
253 
254         refresh();
255 
256         m_manageButton = createManageButton(Constants::TOOLCHAIN_SETTINGS_PAGE_ID);
257     }
258 
~ToolChainKitAspectWidget()259     ~ToolChainKitAspectWidget() override
260     {
261         delete m_mainWidget;
262         delete m_manageButton;
263     }
264 
265 private:
addToLayout(LayoutBuilder & builder)266     void addToLayout(LayoutBuilder &builder) override
267     {
268         addMutableAction(m_mainWidget);
269         builder.addItem(m_mainWidget);
270         builder.addItem(m_manageButton);
271     }
272 
refresh()273     void refresh() override
274     {
275         m_ignoreChanges = true;
276         foreach (Utils::Id l, m_languageComboboxMap.keys()) {
277             const QList<ToolChain *> ltcList
278                     = ToolChainManager::toolChains(Utils::equal(&ToolChain::language, l));
279 
280             QComboBox *cb = m_languageComboboxMap.value(l);
281             cb->clear();
282             cb->addItem(tr("<No compiler>"), QByteArray());
283 
284             foreach (ToolChain *tc, ltcList)
285                 cb->addItem(tc->displayName(), tc->id());
286 
287             cb->setEnabled(cb->count() > 1 && !m_isReadOnly);
288             const int index = indexOf(cb, ToolChainKitAspect::toolChain(m_kit, l));
289             cb->setCurrentIndex(index);
290         }
291         m_ignoreChanges = false;
292     }
293 
makeReadOnly()294     void makeReadOnly() override
295     {
296         m_isReadOnly = true;
297         foreach (Utils::Id l, m_languageComboboxMap.keys()) {
298             m_languageComboboxMap.value(l)->setEnabled(false);
299         }
300     }
301 
currentToolChainChanged(Utils::Id language,int idx)302     void currentToolChainChanged(Utils::Id language, int idx)
303     {
304         if (m_ignoreChanges || idx < 0)
305             return;
306 
307         const QByteArray id = m_languageComboboxMap.value(language)->itemData(idx).toByteArray();
308         ToolChain *tc = ToolChainManager::findToolChain(id);
309         QTC_ASSERT(!tc || tc->language() == language, return);
310         if (tc)
311             ToolChainKitAspect::setToolChain(m_kit, tc);
312         else
313             ToolChainKitAspect::clearToolChain(m_kit, language);
314     }
315 
indexOf(QComboBox * cb,const ToolChain * tc)316     int indexOf(QComboBox *cb, const ToolChain *tc)
317     {
318         const QByteArray id = tc ? tc->id() : QByteArray();
319         for (int i = 0; i < cb->count(); ++i) {
320             if (id == cb->itemData(i).toByteArray())
321                 return i;
322         }
323         return -1;
324     }
325 
326     QWidget *m_mainWidget = nullptr;
327     QWidget *m_manageButton = nullptr;
328     QHash<Utils::Id, QComboBox *> m_languageComboboxMap;
329     bool m_ignoreChanges = false;
330     bool m_isReadOnly = false;
331 };
332 } // namespace Internal
333 
ToolChainKitAspect()334 ToolChainKitAspect::ToolChainKitAspect()
335 {
336     setObjectName(QLatin1String("ToolChainInformation"));
337     setId(ToolChainKitAspect::id());
338     setDisplayName(tr("Compiler"));
339     setDescription(tr("The compiler to use for building.<br>"
340                       "Make sure the compiler will produce binaries compatible "
341                       "with the target device, Qt version and other libraries used."));
342     setPriority(30000);
343 
344     connect(KitManager::instance(), &KitManager::kitsLoaded,
345             this, &ToolChainKitAspect::kitsWereLoaded);
346 }
347 
348 // language id -> tool chain id
defaultToolChainIds()349 static QMap<Utils::Id, QByteArray> defaultToolChainIds()
350 {
351     QMap<Utils::Id, QByteArray> toolChains;
352     const Abi abi = Abi::hostAbi();
353     QList<ToolChain *> tcList = ToolChainManager::toolChains(Utils::equal(&ToolChain::targetAbi, abi));
354     foreach (Utils::Id l, ToolChainManager::allLanguages()) {
355         ToolChain *tc = Utils::findOrDefault(tcList, Utils::equal(&ToolChain::language, l));
356         toolChains.insert(l, tc ? tc->id() : QByteArray());
357     }
358     return toolChains;
359 }
360 
defaultToolChainValue()361 static QVariant defaultToolChainValue()
362 {
363     const QMap<Utils::Id, QByteArray> toolChains = defaultToolChainIds();
364     QVariantMap result;
365     auto end = toolChains.end();
366     for (auto it = toolChains.begin(); it != end; ++it) {
367         result.insert(it.key().toString(), it.value());
368     }
369     return result;
370 }
371 
validate(const Kit * k) const372 Tasks ToolChainKitAspect::validate(const Kit *k) const
373 {
374     Tasks result;
375 
376     const QList<ToolChain*> tcList = toolChains(k);
377     if (tcList.isEmpty()) {
378         result << BuildSystemTask(Task::Warning, ToolChainKitAspect::msgNoToolChainInTarget());
379     } else {
380         QSet<Abi> targetAbis;
381         foreach (ToolChain *tc, tcList) {
382             targetAbis.insert(tc->targetAbi());
383             result << tc->validateKit(k);
384         }
385         if (targetAbis.count() != 1) {
386             result << BuildSystemTask(Task::Error,
387                         tr("Compilers produce code for different ABIs: %1")
388                            .arg(Utils::transform<QList>(targetAbis, &Abi::toString).join(", ")));
389         }
390     }
391     return result;
392 }
393 
upgrade(Kit * k)394 void ToolChainKitAspect::upgrade(Kit *k)
395 {
396     QTC_ASSERT(k, return);
397 
398     const Utils::Id oldIdV1 = KITINFORMATION_ID_V1;
399     const Utils::Id oldIdV2 = KITINFORMATION_ID_V2;
400 
401     // upgrade <=4.1 to 4.2 (keep old settings around for now)
402     {
403         const QVariant oldValue = k->value(oldIdV1);
404         const QVariant value = k->value(oldIdV2);
405         if (value.isNull() && !oldValue.isNull()) {
406             QVariantMap newValue;
407             if (oldValue.type() == QVariant::Map) {
408                 // Used between 4.1 and 4.2:
409                 newValue = oldValue.toMap();
410             } else {
411                 // Used up to 4.1:
412                 newValue.insert(Deprecated::Toolchain::languageId(Deprecated::Toolchain::Cxx), oldValue.toString());
413 
414                 const Utils::Id typeId = DeviceTypeKitAspect::deviceTypeId(k);
415                 if (typeId == Constants::DESKTOP_DEVICE_TYPE) {
416                     // insert default C compiler which did not exist before
417                     newValue.insert(Deprecated::Toolchain::languageId(Deprecated::Toolchain::C),
418                                     defaultToolChainIds().value(Utils::Id(Constants::C_LANGUAGE_ID)));
419                 }
420             }
421             k->setValue(oldIdV2, newValue);
422             k->setSticky(oldIdV2, k->isSticky(oldIdV1));
423         }
424     }
425 
426     // upgrade 4.2 to 4.3 (keep old settings around for now)
427     {
428         const QVariant oldValue = k->value(oldIdV2);
429         const QVariant value = k->value(ToolChainKitAspect::id());
430         if (value.isNull() && !oldValue.isNull()) {
431             QVariantMap newValue = oldValue.toMap();
432             QVariantMap::iterator it = newValue.find(Deprecated::Toolchain::languageId(Deprecated::Toolchain::C));
433             if (it != newValue.end())
434                 newValue.insert(Utils::Id(Constants::C_LANGUAGE_ID).toString(), it.value());
435             it = newValue.find(Deprecated::Toolchain::languageId(Deprecated::Toolchain::Cxx));
436             if (it != newValue.end())
437                 newValue.insert(Utils::Id(Constants::CXX_LANGUAGE_ID).toString(), it.value());
438             k->setValue(ToolChainKitAspect::id(), newValue);
439             k->setSticky(ToolChainKitAspect::id(), k->isSticky(oldIdV2));
440         }
441     }
442 
443     // upgrade 4.3-temporary-master-state to 4.3:
444     {
445         const QVariantMap valueMap = k->value(ToolChainKitAspect::id()).toMap();
446         QVariantMap result;
447         for (const QString &key : valueMap.keys()) {
448             const int pos = key.lastIndexOf('.');
449             if (pos >= 0)
450                 result.insert(key.mid(pos + 1), valueMap.value(key));
451             else
452                 result.insert(key, valueMap.value(key));
453         }
454         k->setValue(ToolChainKitAspect::id(), result);
455     }
456 }
457 
fix(Kit * k)458 void ToolChainKitAspect::fix(Kit *k)
459 {
460     QTC_ASSERT(ToolChainManager::isLoaded(), return);
461     foreach (const Utils::Id& l, ToolChainManager::allLanguages()) {
462         const QByteArray tcId = toolChainId(k, l);
463         if (!tcId.isEmpty() && !ToolChainManager::findToolChain(tcId)) {
464             qWarning("Tool chain set up in kit \"%s\" for \"%s\" not found.",
465                      qPrintable(k->displayName()),
466                      qPrintable(ToolChainManager::displayNameOfLanguageId(l)));
467             clearToolChain(k, l); // make sure to clear out no longer known tool chains
468         }
469     }
470 }
471 
findLanguage(const QString & ls)472 static Utils::Id findLanguage(const QString &ls)
473 {
474     QString lsUpper = ls.toUpper();
475     return Utils::findOrDefault(ToolChainManager::allLanguages(),
476                          [lsUpper](Utils::Id l) { return lsUpper == l.toString().toUpper(); });
477 }
478 
setup(Kit * k)479 void ToolChainKitAspect::setup(Kit *k)
480 {
481     QTC_ASSERT(ToolChainManager::isLoaded(), return);
482     QTC_ASSERT(k, return);
483 
484     QVariantMap value = k->value(id()).toMap();
485     bool lockToolchains = k->isSdkProvided() && !value.isEmpty();
486     if (value.empty())
487         value = defaultToolChainValue().toMap();
488 
489     for (auto i = value.constBegin(); i != value.constEnd(); ++i) {
490         Utils::Id l = findLanguage(i.key());
491 
492         if (!l.isValid()) {
493             lockToolchains = false;
494             continue;
495         }
496 
497         const QByteArray id = i.value().toByteArray();
498         ToolChain *tc = ToolChainManager::findToolChain(id);
499         if (tc)
500             continue;
501 
502         // ID is not found: Might be an ABI string...
503         lockToolchains = false;
504         const QString abi = QString::fromUtf8(id);
505         QList<ToolChain *> possibleTcs = ToolChainManager::toolChains(
506             [abi, l](const ToolChain *t) {
507                 return t->targetAbi().toString() == abi && t->language() == l;
508             });
509         Utils::sort(possibleTcs, [](const ToolChain *tc1, const ToolChain *tc2) {
510             return tc1->hostPrefersToolchain() && !tc2->hostPrefersToolchain();
511         });
512         if (!possibleTcs.isEmpty())
513             setToolChain(k, possibleTcs.first());
514         else
515             clearToolChain(k, l);
516     }
517 
518     k->setSticky(id(), lockToolchains);
519 }
520 
createConfigWidget(Kit * k) const521 KitAspectWidget *ToolChainKitAspect::createConfigWidget(Kit *k) const
522 {
523     QTC_ASSERT(k, return nullptr);
524     return new Internal::ToolChainKitAspectWidget(k, this);
525 }
526 
displayNamePostfix(const Kit * k) const527 QString ToolChainKitAspect::displayNamePostfix(const Kit *k) const
528 {
529     ToolChain *tc = cxxToolChain(k);
530     return tc ? tc->displayName() : QString();
531 }
532 
toUserOutput(const Kit * k) const533 KitAspect::ItemList ToolChainKitAspect::toUserOutput(const Kit *k) const
534 {
535     ToolChain *tc = cxxToolChain(k);
536     return {{tr("Compiler"), tc ? tc->displayName() : tr("None")}};
537 }
538 
addToBuildEnvironment(const Kit * k,Utils::Environment & env) const539 void ToolChainKitAspect::addToBuildEnvironment(const Kit *k, Utils::Environment &env) const
540 {
541     ToolChain *tc = cxxToolChain(k);
542     if (tc)
543         tc->addToEnvironment(env);
544 }
545 
addToMacroExpander(Kit * kit,Utils::MacroExpander * expander) const546 void ToolChainKitAspect::addToMacroExpander(Kit *kit, Utils::MacroExpander *expander) const
547 {
548     QTC_ASSERT(kit, return);
549 
550     // Compatibility with Qt Creator < 4.2:
551     expander->registerVariable("Compiler:Name", tr("Compiler"),
552                                [kit] {
553                                    const ToolChain *tc = cxxToolChain(kit);
554                                    return tc ? tc->displayName() : tr("None");
555                                });
556 
557     expander->registerVariable("Compiler:Executable", tr("Path to the compiler executable"),
558                                [kit] {
559                                    const ToolChain *tc = cxxToolChain(kit);
560                                    return tc ? tc->compilerCommand().path() : QString();
561                                });
562 
563     // After 4.2
564     expander->registerPrefix("Compiler:Name", tr("Compiler for different languages"),
565                              [kit](const QString &ls) {
566                                  const ToolChain *tc = toolChain(kit, findLanguage(ls));
567                                  return tc ? tc->displayName() : tr("None");
568                              });
569     expander->registerPrefix("Compiler:Executable", tr("Compiler executable for different languages"),
570                              [kit](const QString &ls) {
571                                  const ToolChain *tc = toolChain(kit, findLanguage(ls));
572                                  return tc ? tc->compilerCommand().path() : QString();
573                              });
574 }
575 
createOutputParsers(const Kit * k) const576 QList<Utils::OutputLineParser *> ToolChainKitAspect::createOutputParsers(const Kit *k) const
577 {
578     for (const Utils::Id langId : {Constants::CXX_LANGUAGE_ID, Constants::C_LANGUAGE_ID}) {
579         if (const ToolChain * const tc = toolChain(k, langId))
580             return tc->createOutputParsers();
581     }
582     return {};
583 }
584 
availableFeatures(const Kit * k) const585 QSet<Utils::Id> ToolChainKitAspect::availableFeatures(const Kit *k) const
586 {
587     QSet<Utils::Id> result;
588     for (ToolChain *tc : toolChains(k))
589         result.insert(tc->typeId().withPrefix("ToolChain."));
590     return result;
591 }
592 
id()593 Utils::Id ToolChainKitAspect::id()
594 {
595     return KITINFORMATION_ID_V3;
596 }
597 
toolChainId(const Kit * k,Utils::Id language)598 QByteArray ToolChainKitAspect::toolChainId(const Kit *k, Utils::Id language)
599 {
600     QTC_ASSERT(ToolChainManager::isLoaded(), return nullptr);
601     if (!k)
602         return QByteArray();
603     QVariantMap value = k->value(ToolChainKitAspect::id()).toMap();
604     return value.value(language.toString(), QByteArray()).toByteArray();
605 }
606 
toolChain(const Kit * k,Utils::Id language)607 ToolChain *ToolChainKitAspect::toolChain(const Kit *k, Utils::Id language)
608 {
609     return ToolChainManager::findToolChain(toolChainId(k, language));
610 }
611 
cToolChain(const Kit * k)612 ToolChain *ToolChainKitAspect::cToolChain(const Kit *k)
613 {
614     return ToolChainManager::findToolChain(toolChainId(k, ProjectExplorer::Constants::C_LANGUAGE_ID));
615 }
616 
cxxToolChain(const Kit * k)617 ToolChain *ToolChainKitAspect::cxxToolChain(const Kit *k)
618 {
619     return ToolChainManager::findToolChain(toolChainId(k, ProjectExplorer::Constants::CXX_LANGUAGE_ID));
620 }
621 
622 
toolChains(const Kit * k)623 QList<ToolChain *> ToolChainKitAspect::toolChains(const Kit *k)
624 {
625     QTC_ASSERT(k, return QList<ToolChain *>());
626 
627     const QVariantMap value = k->value(ToolChainKitAspect::id()).toMap();
628     const QList<ToolChain *> tcList
629             = Utils::transform<QList>(ToolChainManager::allLanguages(),
630                                [&value](Utils::Id l) -> ToolChain * {
631                                    return ToolChainManager::findToolChain(value.value(l.toString()).toByteArray());
632                                });
633     return Utils::filtered(tcList, [](ToolChain *tc) { return tc; });
634 }
635 
setToolChain(Kit * k,ToolChain * tc)636 void ToolChainKitAspect::setToolChain(Kit *k, ToolChain *tc)
637 {
638     QTC_ASSERT(tc, return);
639     QTC_ASSERT(k, return);
640     QVariantMap result = k->value(ToolChainKitAspect::id()).toMap();
641     result.insert(tc->language().toString(), tc->id());
642 
643     k->setValue(id(), result);
644 }
645 
646 /**
647  * @brief ToolChainKitAspect::setAllToolChainsToMatch
648  *
649  * Set up all toolchains to be similar to the one toolchain provided. Similar ideally means
650  * that all toolchains use the "same" compiler from the same installation, but we will
651  * settle for a toolchain with a matching API instead.
652  *
653  * @param k The kit to set up
654  * @param tc The toolchain to match other languages for.
655  */
setAllToolChainsToMatch(Kit * k,ToolChain * tc)656 void ToolChainKitAspect::setAllToolChainsToMatch(Kit *k, ToolChain *tc)
657 {
658     QTC_ASSERT(tc, return);
659     QTC_ASSERT(k, return);
660 
661     const QList<ToolChain *> allTcList = ToolChainManager::toolChains();
662     QTC_ASSERT(allTcList.contains(tc), return);
663 
664     QVariantMap result = k->value(ToolChainKitAspect::id()).toMap();
665     result.insert(tc->language().toString(), tc->id());
666 
667     for (Utils::Id l : ToolChainManager::allLanguages()) {
668         if (l == tc->language())
669             continue;
670 
671         ToolChain *match = nullptr;
672         ToolChain *bestMatch = nullptr;
673         for (ToolChain *other : allTcList) {
674             if (!other->isValid() || other->language() != l)
675                 continue;
676             if (other->targetAbi() == tc->targetAbi())
677                 match = other;
678             if (match == other
679                     && other->compilerCommand().parentDir() == tc->compilerCommand().parentDir()) {
680                 bestMatch = other;
681                 break;
682             }
683         }
684         if (bestMatch)
685             result.insert(l.toString(), bestMatch->id());
686         else if (match)
687             result.insert(l.toString(), match->id());
688         else
689             result.insert(l.toString(), QByteArray());
690     }
691 
692     k->setValue(id(), result);
693 }
694 
clearToolChain(Kit * k,Utils::Id language)695 void ToolChainKitAspect::clearToolChain(Kit *k, Utils::Id language)
696 {
697     QTC_ASSERT(language.isValid(), return);
698     QTC_ASSERT(k, return);
699 
700     QVariantMap result = k->value(ToolChainKitAspect::id()).toMap();
701     result.insert(language.toString(), QByteArray());
702     k->setValue(id(), result);
703 }
704 
targetAbi(const Kit * k)705 Abi ToolChainKitAspect::targetAbi(const Kit *k)
706 {
707     QList<ToolChain *> tcList = toolChains(k);
708     // Find the best possible ABI for all the tool chains...
709     Abi cxxAbi;
710     QHash<Abi, int> abiCount;
711     foreach (ToolChain *tc, tcList) {
712         Abi ta = tc->targetAbi();
713         if (tc->language() == Utils::Id(Constants::CXX_LANGUAGE_ID))
714             cxxAbi = tc->targetAbi();
715         abiCount[ta] = (abiCount.contains(ta) ? abiCount[ta] + 1 : 1);
716     }
717     QVector<Abi> candidates;
718     int count = -1;
719     candidates.reserve(tcList.count());
720     for (auto i = abiCount.begin(); i != abiCount.end(); ++i) {
721         if (i.value() > count) {
722             candidates.clear();
723             candidates.append(i.key());
724             count = i.value();
725         } else if (i.value() == count) {
726             candidates.append(i.key());
727         }
728     }
729 
730     // Found a good candidate:
731     if (candidates.isEmpty())
732         return Abi::hostAbi();
733     if (candidates.contains(cxxAbi)) // Use Cxx compiler as a tie breaker
734         return cxxAbi;
735     return candidates.at(0); // Use basically a random Abi...
736 }
737 
msgNoToolChainInTarget()738 QString ToolChainKitAspect::msgNoToolChainInTarget()
739 {
740     return tr("No compiler set in kit.");
741 }
742 
kitsWereLoaded()743 void ToolChainKitAspect::kitsWereLoaded()
744 {
745     foreach (Kit *k, KitManager::kits())
746         fix(k);
747 
748     connect(ToolChainManager::instance(), &ToolChainManager::toolChainRemoved,
749             this, &ToolChainKitAspect::toolChainRemoved);
750     connect(ToolChainManager::instance(), &ToolChainManager::toolChainUpdated,
751             this, &ToolChainKitAspect::toolChainUpdated);
752 }
753 
toolChainUpdated(ToolChain * tc)754 void ToolChainKitAspect::toolChainUpdated(ToolChain *tc)
755 {
756     for (Kit *k : KitManager::kits()) {
757         if (toolChain(k, tc->language()) == tc)
758             notifyAboutUpdate(k);
759     }
760 }
761 
toolChainRemoved(ToolChain * tc)762 void ToolChainKitAspect::toolChainRemoved(ToolChain *tc)
763 {
764     Q_UNUSED(tc)
765     foreach (Kit *k, KitManager::kits())
766         fix(k);
767 }
768 
769 // --------------------------------------------------------------------------
770 // DeviceTypeKitAspect:
771 // --------------------------------------------------------------------------
772 namespace Internal {
773 class DeviceTypeKitAspectWidget final : public KitAspectWidget
774 {
775     Q_DECLARE_TR_FUNCTIONS(ProjectExplorer::DeviceTypeKitAspect)
776 
777 public:
DeviceTypeKitAspectWidget(Kit * workingCopy,const KitAspect * ki)778     DeviceTypeKitAspectWidget(Kit *workingCopy, const KitAspect *ki)
779         : KitAspectWidget(workingCopy, ki), m_comboBox(createSubWidget<QComboBox>())
780     {
781         for (IDeviceFactory *factory : IDeviceFactory::allDeviceFactories())
782             m_comboBox->addItem(factory->displayName(), factory->deviceType().toSetting());
783         m_comboBox->setToolTip(ki->description());
784         refresh();
785         connect(m_comboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
786                 this, &DeviceTypeKitAspectWidget::currentTypeChanged);
787     }
788 
~DeviceTypeKitAspectWidget()789     ~DeviceTypeKitAspectWidget() override { delete m_comboBox; }
790 
791 private:
addToLayout(LayoutBuilder & builder)792     void addToLayout(LayoutBuilder &builder) override
793     {
794         addMutableAction(m_comboBox);
795         builder.addItem(m_comboBox);
796     }
797 
makeReadOnly()798     void makeReadOnly() override { m_comboBox->setEnabled(false); }
799 
refresh()800     void refresh() override
801     {
802         Utils::Id devType = DeviceTypeKitAspect::deviceTypeId(m_kit);
803         if (!devType.isValid())
804             m_comboBox->setCurrentIndex(-1);
805         for (int i = 0; i < m_comboBox->count(); ++i) {
806             if (m_comboBox->itemData(i) == devType.toSetting()) {
807                 m_comboBox->setCurrentIndex(i);
808                 break;
809             }
810         }
811     }
812 
currentTypeChanged(int idx)813     void currentTypeChanged(int idx)
814     {
815         Utils::Id type = idx < 0 ? Utils::Id() : Utils::Id::fromSetting(m_comboBox->itemData(idx));
816         DeviceTypeKitAspect::setDeviceTypeId(m_kit, type);
817     }
818 
819     QComboBox *m_comboBox;
820 };
821 } // namespace Internal
822 
DeviceTypeKitAspect()823 DeviceTypeKitAspect::DeviceTypeKitAspect()
824 {
825     setObjectName(QLatin1String("DeviceTypeInformation"));
826     setId(DeviceTypeKitAspect::id());
827     setDisplayName(tr("Device type"));
828     setDescription(tr("The type of device to run applications on."));
829     setPriority(33000);
830     makeEssential();
831 }
832 
setup(Kit * k)833 void DeviceTypeKitAspect::setup(Kit *k)
834 {
835     if (k && !k->hasValue(id()))
836         k->setValue(id(), QByteArray(Constants::DESKTOP_DEVICE_TYPE));
837 }
838 
validate(const Kit * k) const839 Tasks DeviceTypeKitAspect::validate(const Kit *k) const
840 {
841     Q_UNUSED(k)
842     return {};
843 }
844 
createConfigWidget(Kit * k) const845 KitAspectWidget *DeviceTypeKitAspect::createConfigWidget(Kit *k) const
846 {
847     QTC_ASSERT(k, return nullptr);
848     return new Internal::DeviceTypeKitAspectWidget(k, this);
849 }
850 
toUserOutput(const Kit * k) const851 KitAspect::ItemList DeviceTypeKitAspect::toUserOutput(const Kit *k) const
852 {
853     QTC_ASSERT(k, return {});
854     Utils::Id type = deviceTypeId(k);
855     QString typeDisplayName = tr("Unknown device type");
856     if (type.isValid()) {
857         if (IDeviceFactory *factory = IDeviceFactory::find(type))
858             typeDisplayName = factory->displayName();
859     }
860     return {{tr("Device type"), typeDisplayName}};
861 }
862 
id()863 const Utils::Id DeviceTypeKitAspect::id()
864 {
865     return "PE.Profile.DeviceType";
866 }
867 
deviceTypeId(const Kit * k)868 const Utils::Id DeviceTypeKitAspect::deviceTypeId(const Kit *k)
869 {
870     return k ? Utils::Id::fromSetting(k->value(DeviceTypeKitAspect::id())) : Utils::Id();
871 }
872 
setDeviceTypeId(Kit * k,Utils::Id type)873 void DeviceTypeKitAspect::setDeviceTypeId(Kit *k, Utils::Id type)
874 {
875     QTC_ASSERT(k, return);
876     k->setValue(DeviceTypeKitAspect::id(), type.toSetting());
877 }
878 
supportedPlatforms(const Kit * k) const879 QSet<Utils::Id> DeviceTypeKitAspect::supportedPlatforms(const Kit *k) const
880 {
881     return {deviceTypeId(k)};
882 }
883 
availableFeatures(const Kit * k) const884 QSet<Utils::Id> DeviceTypeKitAspect::availableFeatures(const Kit *k) const
885 {
886     Utils::Id id = DeviceTypeKitAspect::deviceTypeId(k);
887     if (id.isValid())
888         return {id.withPrefix("DeviceType.")};
889     return QSet<Utils::Id>();
890 }
891 
892 // --------------------------------------------------------------------------
893 // DeviceKitAspect:
894 // --------------------------------------------------------------------------
895 namespace Internal {
896 class DeviceKitAspectWidget final : public KitAspectWidget
897 {
898     Q_DECLARE_TR_FUNCTIONS(ProjectExplorer::DeviceKitAspect)
899 
900 public:
DeviceKitAspectWidget(Kit * workingCopy,const KitAspect * ki)901     DeviceKitAspectWidget(Kit *workingCopy, const KitAspect *ki)
902         : KitAspectWidget(workingCopy, ki),
903         m_comboBox(createSubWidget<QComboBox>()),
904         m_model(new DeviceManagerModel(DeviceManager::instance()))
905     {
906         m_comboBox->setSizePolicy(QSizePolicy::Ignored, m_comboBox->sizePolicy().verticalPolicy());
907         m_comboBox->setModel(m_model);
908         m_manageButton = createManageButton(Constants::DEVICE_SETTINGS_PAGE_ID);
909         refresh();
910         m_comboBox->setToolTip(ki->description());
911 
912         connect(m_model, &QAbstractItemModel::modelAboutToBeReset,
913                 this, &DeviceKitAspectWidget::modelAboutToReset);
914         connect(m_model, &QAbstractItemModel::modelReset,
915                 this, &DeviceKitAspectWidget::modelReset);
916         connect(m_comboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
917                 this, &DeviceKitAspectWidget::currentDeviceChanged);
918     }
919 
~DeviceKitAspectWidget()920     ~DeviceKitAspectWidget() override
921     {
922         delete m_comboBox;
923         delete m_model;
924         delete m_manageButton;
925     }
926 
927 private:
addToLayout(LayoutBuilder & builder)928     void addToLayout(LayoutBuilder &builder) override
929     {
930         addMutableAction(m_comboBox);
931         builder.addItem(m_comboBox);
932         builder.addItem(m_manageButton);
933     }
934 
makeReadOnly()935     void makeReadOnly() override { m_comboBox->setEnabled(false); }
936 
refresh()937     void refresh() override
938     {
939         m_model->setTypeFilter(DeviceTypeKitAspect::deviceTypeId(m_kit));
940         m_comboBox->setCurrentIndex(m_model->indexOf(DeviceKitAspect::device(m_kit)));
941     }
942 
modelAboutToReset()943     void modelAboutToReset()
944     {
945         m_selectedId = m_model->deviceId(m_comboBox->currentIndex());
946         m_ignoreChange = true;
947     }
948 
modelReset()949     void modelReset()
950     {
951         m_comboBox->setCurrentIndex(m_model->indexForId(m_selectedId));
952         m_ignoreChange = false;
953     }
954 
currentDeviceChanged()955     void currentDeviceChanged()
956     {
957         if (m_ignoreChange)
958             return;
959         DeviceKitAspect::setDeviceId(m_kit, m_model->deviceId(m_comboBox->currentIndex()));
960     }
961 
962     bool m_ignoreChange = false;
963     QComboBox *m_comboBox;
964     QWidget *m_manageButton;
965     DeviceManagerModel *m_model;
966     Utils::Id m_selectedId;
967 };
968 } // namespace Internal
969 
DeviceKitAspect()970 DeviceKitAspect::DeviceKitAspect()
971 {
972     setObjectName(QLatin1String("DeviceInformation"));
973     setId(DeviceKitAspect::id());
974     setDisplayName(tr("Device"));
975     setDescription(tr("The device to run the applications on."));
976     setPriority(32000);
977 
978     connect(KitManager::instance(), &KitManager::kitsLoaded,
979             this, &DeviceKitAspect::kitsWereLoaded);
980 }
981 
defaultValue(const Kit * k) const982 QVariant DeviceKitAspect::defaultValue(const Kit *k) const
983 {
984     Utils::Id type = DeviceTypeKitAspect::deviceTypeId(k);
985     // Use default device if that is compatible:
986     IDevice::ConstPtr dev = DeviceManager::instance()->defaultDevice(type);
987     if (dev && dev->isCompatibleWith(k))
988         return dev->id().toString();
989     // Use any other device that is compatible:
990     for (int i = 0; i < DeviceManager::instance()->deviceCount(); ++i) {
991         dev = DeviceManager::instance()->deviceAt(i);
992         if (dev && dev->isCompatibleWith(k))
993             return dev->id().toString();
994     }
995     // Fail: No device set up.
996     return QString();
997 }
998 
validate(const Kit * k) const999 Tasks DeviceKitAspect::validate(const Kit *k) const
1000 {
1001     IDevice::ConstPtr dev = DeviceKitAspect::device(k);
1002     Tasks result;
1003     if (dev.isNull())
1004         result.append(BuildSystemTask(Task::Warning, tr("No device set.")));
1005     else if (!dev->isCompatibleWith(k))
1006         result.append(BuildSystemTask(Task::Error, tr("Device is incompatible with this kit.")));
1007 
1008     return result;
1009 }
1010 
fix(Kit * k)1011 void DeviceKitAspect::fix(Kit *k)
1012 {
1013     IDevice::ConstPtr dev = DeviceKitAspect::device(k);
1014     if (!dev.isNull() && !dev->isCompatibleWith(k)) {
1015         qWarning("Device is no longer compatible with kit \"%s\", removing it.",
1016                  qPrintable(k->displayName()));
1017         setDeviceId(k, Utils::Id());
1018     }
1019 }
1020 
setup(Kit * k)1021 void DeviceKitAspect::setup(Kit *k)
1022 {
1023     QTC_ASSERT(DeviceManager::instance()->isLoaded(), return);
1024     IDevice::ConstPtr dev = DeviceKitAspect::device(k);
1025     if (!dev.isNull() && dev->isCompatibleWith(k))
1026         return;
1027 
1028     setDeviceId(k, Utils::Id::fromSetting(defaultValue(k)));
1029 }
1030 
createConfigWidget(Kit * k) const1031 KitAspectWidget *DeviceKitAspect::createConfigWidget(Kit *k) const
1032 {
1033     QTC_ASSERT(k, return nullptr);
1034     return new Internal::DeviceKitAspectWidget(k, this);
1035 }
1036 
displayNamePostfix(const Kit * k) const1037 QString DeviceKitAspect::displayNamePostfix(const Kit *k) const
1038 {
1039     IDevice::ConstPtr dev = device(k);
1040     return dev.isNull() ? QString() : dev->displayName();
1041 }
1042 
toUserOutput(const Kit * k) const1043 KitAspect::ItemList DeviceKitAspect::toUserOutput(const Kit *k) const
1044 {
1045     IDevice::ConstPtr dev = device(k);
1046     return {{tr("Device"), dev.isNull() ? tr("Unconfigured") : dev->displayName()}};
1047 }
1048 
addToMacroExpander(Kit * kit,Utils::MacroExpander * expander) const1049 void DeviceKitAspect::addToMacroExpander(Kit *kit, Utils::MacroExpander *expander) const
1050 {
1051     QTC_ASSERT(kit, return);
1052     expander->registerVariable("Device:HostAddress", tr("Host address"),
1053         [kit]() -> QString {
1054             const IDevice::ConstPtr device = DeviceKitAspect::device(kit);
1055             return device ? device->sshParameters().host() : QString();
1056     });
1057     expander->registerVariable("Device:SshPort", tr("SSH port"),
1058         [kit]() -> QString {
1059             const IDevice::ConstPtr device = DeviceKitAspect::device(kit);
1060             return device ? QString::number(device->sshParameters().port()) : QString();
1061     });
1062     expander->registerVariable("Device:UserName", tr("User name"),
1063         [kit]() -> QString {
1064             const IDevice::ConstPtr device = DeviceKitAspect::device(kit);
1065             return device ? device->sshParameters().userName() : QString();
1066     });
1067     expander->registerVariable("Device:KeyFile", tr("Private key file"),
1068         [kit]() -> QString {
1069             const IDevice::ConstPtr device = DeviceKitAspect::device(kit);
1070             return device ? device->sshParameters().privateKeyFile : QString();
1071     });
1072     expander->registerVariable("Device:Name", tr("Device name"),
1073         [kit]() -> QString {
1074             const IDevice::ConstPtr device = DeviceKitAspect::device(kit);
1075             return device ? device->displayName() : QString();
1076     });
1077 }
1078 
id()1079 Utils::Id DeviceKitAspect::id()
1080 {
1081     return "PE.Profile.Device";
1082 }
1083 
device(const Kit * k)1084 IDevice::ConstPtr DeviceKitAspect::device(const Kit *k)
1085 {
1086     QTC_ASSERT(DeviceManager::instance()->isLoaded(), return IDevice::ConstPtr());
1087     return DeviceManager::instance()->find(deviceId(k));
1088 }
1089 
deviceId(const Kit * k)1090 Utils::Id DeviceKitAspect::deviceId(const Kit *k)
1091 {
1092     return k ? Utils::Id::fromSetting(k->value(DeviceKitAspect::id())) : Utils::Id();
1093 }
1094 
setDevice(Kit * k,IDevice::ConstPtr dev)1095 void DeviceKitAspect::setDevice(Kit *k, IDevice::ConstPtr dev)
1096 {
1097     setDeviceId(k, dev ? dev->id() : Utils::Id());
1098 }
1099 
setDeviceId(Kit * k,Utils::Id id)1100 void DeviceKitAspect::setDeviceId(Kit *k, Utils::Id id)
1101 {
1102     QTC_ASSERT(k, return);
1103     k->setValue(DeviceKitAspect::id(), id.toSetting());
1104 }
1105 
kitsWereLoaded()1106 void DeviceKitAspect::kitsWereLoaded()
1107 {
1108     foreach (Kit *k, KitManager::kits())
1109         fix(k);
1110 
1111     DeviceManager *dm = DeviceManager::instance();
1112     connect(dm, &DeviceManager::deviceListReplaced, this, &DeviceKitAspect::devicesChanged);
1113     connect(dm, &DeviceManager::deviceAdded, this, &DeviceKitAspect::devicesChanged);
1114     connect(dm, &DeviceManager::deviceRemoved, this, &DeviceKitAspect::devicesChanged);
1115     connect(dm, &DeviceManager::deviceUpdated, this, &DeviceKitAspect::deviceUpdated);
1116 
1117     connect(KitManager::instance(), &KitManager::kitUpdated,
1118             this, &DeviceKitAspect::kitUpdated);
1119     connect(KitManager::instance(), &KitManager::unmanagedKitUpdated,
1120             this, &DeviceKitAspect::kitUpdated);
1121 }
1122 
deviceUpdated(Utils::Id id)1123 void DeviceKitAspect::deviceUpdated(Utils::Id id)
1124 {
1125     foreach (Kit *k, KitManager::kits()) {
1126         if (deviceId(k) == id)
1127             notifyAboutUpdate(k);
1128     }
1129 }
1130 
kitUpdated(Kit * k)1131 void DeviceKitAspect::kitUpdated(Kit *k)
1132 {
1133     setup(k); // Set default device if necessary
1134 }
1135 
devicesChanged()1136 void DeviceKitAspect::devicesChanged()
1137 {
1138     foreach (Kit *k, KitManager::kits())
1139         setup(k); // Set default device if necessary
1140 }
1141 
1142 // --------------------------------------------------------------------------
1143 // BuildDeviceKitAspect:
1144 // --------------------------------------------------------------------------
1145 namespace Internal {
1146 class BuildDeviceKitAspectWidget final : public KitAspectWidget
1147 {
1148     Q_DECLARE_TR_FUNCTIONS(ProjectExplorer::BuildDeviceKitAspect)
1149 
1150 public:
BuildDeviceKitAspectWidget(Kit * workingCopy,const KitAspect * ki)1151     BuildDeviceKitAspectWidget(Kit *workingCopy, const KitAspect *ki)
1152         : KitAspectWidget(workingCopy, ki),
1153         m_comboBox(createSubWidget<QComboBox>()),
1154         m_model(new DeviceManagerModel(DeviceManager::instance()))
1155     {
1156         m_comboBox->setSizePolicy(QSizePolicy::Ignored, m_comboBox->sizePolicy().verticalPolicy());
1157         m_comboBox->setModel(m_model);
1158         m_manageButton = createManageButton(Constants::DEVICE_SETTINGS_PAGE_ID);
1159         refresh();
1160         m_comboBox->setToolTip(ki->description());
1161 
1162         connect(m_model, &QAbstractItemModel::modelAboutToBeReset,
1163                 this, &BuildDeviceKitAspectWidget::modelAboutToReset);
1164         connect(m_model, &QAbstractItemModel::modelReset,
1165                 this, &BuildDeviceKitAspectWidget::modelReset);
1166         connect(m_comboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
1167                 this, &BuildDeviceKitAspectWidget::currentDeviceChanged);
1168     }
1169 
~BuildDeviceKitAspectWidget()1170     ~BuildDeviceKitAspectWidget() override
1171     {
1172         delete m_comboBox;
1173         delete m_model;
1174         delete m_manageButton;
1175     }
1176 
1177 private:
addToLayout(LayoutBuilder & builder)1178     void addToLayout(LayoutBuilder &builder) override
1179     {
1180         addMutableAction(m_comboBox);
1181         builder.addItem(m_comboBox);
1182         builder.addItem(m_manageButton);
1183     }
1184 
makeReadOnly()1185     void makeReadOnly() override { m_comboBox->setEnabled(false); }
1186 
refresh()1187     void refresh() override
1188     {
1189         QList<Utils::Id> blackList;
1190         const DeviceManager *dm = DeviceManager::instance();
1191         for (int i = 0; i < dm->deviceCount(); ++i) {
1192             IDevice::ConstPtr device = dm->deviceAt(i);
1193             if (!(device->type() == Constants::DESKTOP_DEVICE_TYPE
1194                     || device->type() == Docker::Constants::DOCKER_DEVICE_TYPE))
1195                 blackList.append(device->id());
1196         }
1197 
1198         m_model->setFilter(blackList);
1199         m_comboBox->setCurrentIndex(m_model->indexOf(BuildDeviceKitAspect::device(m_kit)));
1200     }
1201 
modelAboutToReset()1202     void modelAboutToReset()
1203     {
1204         m_selectedId = m_model->deviceId(m_comboBox->currentIndex());
1205         m_ignoreChange = true;
1206     }
1207 
modelReset()1208     void modelReset()
1209     {
1210         m_comboBox->setCurrentIndex(m_model->indexForId(m_selectedId));
1211         m_ignoreChange = false;
1212     }
1213 
currentDeviceChanged()1214     void currentDeviceChanged()
1215     {
1216         if (m_ignoreChange)
1217             return;
1218         BuildDeviceKitAspect::setDeviceId(m_kit, m_model->deviceId(m_comboBox->currentIndex()));
1219     }
1220 
1221     bool m_ignoreChange = false;
1222     QComboBox *m_comboBox;
1223     QWidget *m_manageButton;
1224     DeviceManagerModel *m_model;
1225     Utils::Id m_selectedId;
1226 };
1227 } // namespace Internal
1228 
BuildDeviceKitAspect()1229 BuildDeviceKitAspect::BuildDeviceKitAspect()
1230 {
1231     setObjectName("BuildDeviceInformation");
1232     setId(BuildDeviceKitAspect::id());
1233     setDisplayName(tr("Build device"));
1234     setDescription(tr("The device used to build applications on."));
1235     setPriority(31900);
1236 
1237     connect(KitManager::instance(), &KitManager::kitsLoaded,
1238             this, &BuildDeviceKitAspect::kitsWereLoaded);
1239 }
1240 
defaultValue(const Kit * k) const1241 QVariant BuildDeviceKitAspect::defaultValue(const Kit *k) const
1242 {
1243     Q_UNUSED(k);
1244     IDevice::ConstPtr defaultDevice = DeviceManager::defaultDesktopDevice();
1245     return defaultDevice->id().toString();
1246 }
1247 
validate(const Kit * k) const1248 Tasks BuildDeviceKitAspect::validate(const Kit *k) const
1249 {
1250     IDevice::ConstPtr dev = BuildDeviceKitAspect::device(k);
1251     Tasks result;
1252     if (dev.isNull())
1253         result.append(BuildSystemTask(Task::Warning, tr("No build device set.")));
1254 
1255     return result;
1256 }
1257 
createConfigWidget(Kit * k) const1258 KitAspectWidget *BuildDeviceKitAspect::createConfigWidget(Kit *k) const
1259 {
1260     QTC_ASSERT(k, return nullptr);
1261     return new Internal::BuildDeviceKitAspectWidget(k, this);
1262 }
1263 
displayNamePostfix(const Kit * k) const1264 QString BuildDeviceKitAspect::displayNamePostfix(const Kit *k) const
1265 {
1266     IDevice::ConstPtr dev = device(k);
1267     return dev.isNull() ? QString() : dev->displayName();
1268 }
1269 
toUserOutput(const Kit * k) const1270 KitAspect::ItemList BuildDeviceKitAspect::toUserOutput(const Kit *k) const
1271 {
1272     IDevice::ConstPtr dev = device(k);
1273     return {{tr("Build device"), dev.isNull() ? tr("Unconfigured") : dev->displayName()}};
1274 }
1275 
addToMacroExpander(Kit * kit,Utils::MacroExpander * expander) const1276 void BuildDeviceKitAspect::addToMacroExpander(Kit *kit, Utils::MacroExpander *expander) const
1277 {
1278     QTC_ASSERT(kit, return);
1279     expander->registerVariable("BuildDevice:HostAddress", tr("Build host address"),
1280         [kit]() -> QString {
1281             const IDevice::ConstPtr device = BuildDeviceKitAspect::device(kit);
1282             return device ? device->sshParameters().host() : QString();
1283     });
1284     expander->registerVariable("BuildDevice:SshPort", tr("Build SSH port"),
1285         [kit]() -> QString {
1286             const IDevice::ConstPtr device = BuildDeviceKitAspect::device(kit);
1287             return device ? QString::number(device->sshParameters().port()) : QString();
1288     });
1289     expander->registerVariable("BuildDevice:UserName", tr("Build user name"),
1290         [kit]() -> QString {
1291             const IDevice::ConstPtr device = BuildDeviceKitAspect::device(kit);
1292             return device ? device->sshParameters().userName() : QString();
1293     });
1294     expander->registerVariable("BuildDevice:KeyFile", tr("Build private key file"),
1295         [kit]() -> QString {
1296             const IDevice::ConstPtr device = BuildDeviceKitAspect::device(kit);
1297             return device ? device->sshParameters().privateKeyFile : QString();
1298     });
1299     expander->registerVariable("BuildDevice:Name", tr("Build device name"),
1300         [kit]() -> QString {
1301             const IDevice::ConstPtr device = BuildDeviceKitAspect::device(kit);
1302             return device ? device->displayName() : QString();
1303     });
1304 }
1305 
id()1306 Utils::Id BuildDeviceKitAspect::id()
1307 {
1308     return "PE.Profile.BuildDevice";
1309 }
1310 
device(const Kit * k)1311 IDevice::ConstPtr BuildDeviceKitAspect::device(const Kit *k)
1312 {
1313     QTC_ASSERT(DeviceManager::instance()->isLoaded(), return IDevice::ConstPtr());
1314     IDevice::ConstPtr dev = DeviceManager::instance()->find(deviceId(k));
1315     // Use the "run" device as fallback if no build device is present.
1316     // FIXME: Think about whether this shouldn't be the other way round.
1317     if (!dev)
1318         dev = DeviceKitAspect::device(k);
1319     return dev;
1320 }
1321 
deviceId(const Kit * k)1322 Utils::Id BuildDeviceKitAspect::deviceId(const Kit *k)
1323 {
1324     return k ? Utils::Id::fromSetting(k->value(BuildDeviceKitAspect::id())) : Utils::Id();
1325 }
1326 
setDevice(Kit * k,IDevice::ConstPtr dev)1327 void BuildDeviceKitAspect::setDevice(Kit *k, IDevice::ConstPtr dev)
1328 {
1329     setDeviceId(k, dev ? dev->id() : Utils::Id());
1330 }
1331 
setDeviceId(Kit * k,Utils::Id id)1332 void BuildDeviceKitAspect::setDeviceId(Kit *k, Utils::Id id)
1333 {
1334     QTC_ASSERT(k, return);
1335     k->setValue(BuildDeviceKitAspect::id(), id.toSetting());
1336 }
1337 
kitsWereLoaded()1338 void BuildDeviceKitAspect::kitsWereLoaded()
1339 {
1340     foreach (Kit *k, KitManager::kits())
1341         fix(k);
1342 
1343     DeviceManager *dm = DeviceManager::instance();
1344     connect(dm, &DeviceManager::deviceListReplaced, this, &BuildDeviceKitAspect::devicesChanged);
1345     connect(dm, &DeviceManager::deviceAdded, this, &BuildDeviceKitAspect::devicesChanged);
1346     connect(dm, &DeviceManager::deviceRemoved, this, &BuildDeviceKitAspect::devicesChanged);
1347     connect(dm, &DeviceManager::deviceUpdated, this, &BuildDeviceKitAspect::deviceUpdated);
1348 
1349     connect(KitManager::instance(), &KitManager::kitUpdated,
1350             this, &BuildDeviceKitAspect::kitUpdated);
1351     connect(KitManager::instance(), &KitManager::unmanagedKitUpdated,
1352             this, &BuildDeviceKitAspect::kitUpdated);
1353 }
1354 
deviceUpdated(Utils::Id id)1355 void BuildDeviceKitAspect::deviceUpdated(Utils::Id id)
1356 {
1357     foreach (Kit *k, KitManager::kits()) {
1358         if (deviceId(k) == id)
1359             notifyAboutUpdate(k);
1360     }
1361 }
1362 
kitUpdated(Kit * k)1363 void BuildDeviceKitAspect::kitUpdated(Kit *k)
1364 {
1365     setup(k); // Set default device if necessary
1366 }
1367 
devicesChanged()1368 void BuildDeviceKitAspect::devicesChanged()
1369 {
1370     foreach (Kit *k, KitManager::kits())
1371         setup(k); // Set default device if necessary
1372 }
1373 
1374 // --------------------------------------------------------------------------
1375 // EnvironmentKitAspect:
1376 // --------------------------------------------------------------------------
1377 namespace Internal {
1378 class EnvironmentKitAspectWidget final : public KitAspectWidget
1379 {
1380     Q_DECLARE_TR_FUNCTIONS(ProjectExplorer::EnvironmentKitAspect)
1381 
1382 public:
EnvironmentKitAspectWidget(Kit * workingCopy,const KitAspect * ki)1383     EnvironmentKitAspectWidget(Kit *workingCopy, const KitAspect *ki)
1384         : KitAspectWidget(workingCopy, ki),
1385           m_summaryLabel(createSubWidget<Utils::ElidingLabel>()),
1386           m_manageButton(createSubWidget<QPushButton>()),
1387           m_mainWidget(createSubWidget<QWidget>())
1388     {
1389         auto *layout = new QVBoxLayout;
1390         layout->setContentsMargins(0, 0, 0, 0);
1391         layout->addWidget(m_summaryLabel);
1392         if (Utils::HostOsInfo::isWindowsHost())
1393             initMSVCOutputSwitch(layout);
1394         m_mainWidget->setLayout(layout);
1395         refresh();
1396         m_manageButton->setText(tr("Change..."));
1397         connect(m_manageButton, &QAbstractButton::clicked,
1398                 this, &EnvironmentKitAspectWidget::editEnvironmentChanges);
1399     }
1400 
1401 private:
addToLayout(LayoutBuilder & builder)1402     void addToLayout(LayoutBuilder &builder) override
1403     {
1404         addMutableAction(m_mainWidget);
1405         builder.addItem(m_mainWidget);
1406         builder.addItem(m_manageButton);
1407     }
1408 
makeReadOnly()1409     void makeReadOnly() override { m_manageButton->setEnabled(false); }
1410 
refresh()1411     void refresh() override
1412     {
1413         const Utils::EnvironmentItems changes = currentEnvironment();
1414         const QString shortSummary = Utils::EnvironmentItem::toStringList(changes).join("; ");
1415         m_summaryLabel->setText(shortSummary.isEmpty() ? tr("No changes to apply.") : shortSummary);
1416     }
1417 
editEnvironmentChanges()1418     void editEnvironmentChanges()
1419     {
1420         Utils::MacroExpander *expander = m_kit->macroExpander();
1421         Utils::EnvironmentDialog::Polisher polisher = [expander](QWidget *w) {
1422             Utils::VariableChooser::addSupportForChildWidgets(w, expander);
1423         };
1424         auto changes = Utils::EnvironmentDialog::getEnvironmentItems(m_summaryLabel,
1425                                                                      currentEnvironment(),
1426                                                                      QString(),
1427                                                                      polisher);
1428         if (!changes)
1429             return;
1430 
1431         if (Utils::HostOsInfo::isWindowsHost()) {
1432             const Utils::EnvironmentItem forceMSVCEnglishItem("VSLANG", "1033");
1433             if (m_vslangCheckbox->isChecked() && changes->indexOf(forceMSVCEnglishItem) < 0)
1434                 changes->append(forceMSVCEnglishItem);
1435         }
1436 
1437         EnvironmentKitAspect::setEnvironmentChanges(m_kit, *changes);
1438     }
1439 
currentEnvironment() const1440     Utils::EnvironmentItems currentEnvironment() const
1441     {
1442         Utils::EnvironmentItems changes = EnvironmentKitAspect::environmentChanges(m_kit);
1443 
1444         if (Utils::HostOsInfo::isWindowsHost()) {
1445             const Utils::EnvironmentItem forceMSVCEnglishItem("VSLANG", "1033");
1446             if (changes.indexOf(forceMSVCEnglishItem) >= 0) {
1447                 m_vslangCheckbox->setCheckState(Qt::Checked);
1448                 changes.removeAll(forceMSVCEnglishItem);
1449             }
1450         }
1451 
1452         Utils::sort(changes, [](const Utils::EnvironmentItem &lhs, const Utils::EnvironmentItem &rhs)
1453         { return QString::localeAwareCompare(lhs.name, rhs.name) < 0; });
1454         return changes;
1455     }
1456 
initMSVCOutputSwitch(QVBoxLayout * layout)1457     void initMSVCOutputSwitch(QVBoxLayout *layout)
1458     {
1459         m_vslangCheckbox = new QCheckBox(tr("Force UTF-8 MSVC compiler output"));
1460         layout->addWidget(m_vslangCheckbox);
1461         m_vslangCheckbox->setToolTip(tr("Either switches MSVC to English or keeps the language and "
1462                                         "just forces UTF-8 output (may vary depending on the used MSVC "
1463                                         "compiler)."));
1464         connect(m_vslangCheckbox, &QCheckBox::toggled, this, [this](bool checked) {
1465             Utils::EnvironmentItems changes = EnvironmentKitAspect::environmentChanges(m_kit);
1466             const Utils::EnvironmentItem forceMSVCEnglishItem("VSLANG", "1033");
1467             if (!checked && changes.indexOf(forceMSVCEnglishItem) >= 0)
1468                 changes.removeAll(forceMSVCEnglishItem);
1469             if (checked && changes.indexOf(forceMSVCEnglishItem) < 0)
1470                 changes.append(forceMSVCEnglishItem);
1471             EnvironmentKitAspect::setEnvironmentChanges(m_kit, changes);
1472         });
1473     }
1474 
1475     Utils::ElidingLabel *m_summaryLabel;
1476     QPushButton *m_manageButton;
1477     QCheckBox *m_vslangCheckbox;
1478     QWidget *m_mainWidget;
1479 };
1480 } // namespace Internal
1481 
EnvironmentKitAspect()1482 EnvironmentKitAspect::EnvironmentKitAspect()
1483 {
1484     setObjectName(QLatin1String("EnvironmentKitAspect"));
1485     setId(EnvironmentKitAspect::id());
1486     setDisplayName(tr("Environment"));
1487     setDescription(tr("Additional build environment settings when using this kit."));
1488     setPriority(29000);
1489 }
1490 
validate(const Kit * k) const1491 Tasks EnvironmentKitAspect::validate(const Kit *k) const
1492 {
1493     Tasks result;
1494     QTC_ASSERT(k, return result);
1495 
1496     const QVariant variant = k->value(EnvironmentKitAspect::id());
1497     if (!variant.isNull() && !variant.canConvert(QVariant::List))
1498         result << BuildSystemTask(Task::Error, tr("The environment setting value is invalid."));
1499 
1500     return result;
1501 }
1502 
fix(Kit * k)1503 void EnvironmentKitAspect::fix(Kit *k)
1504 {
1505     QTC_ASSERT(k, return);
1506 
1507     const QVariant variant = k->value(EnvironmentKitAspect::id());
1508     if (!variant.isNull() && !variant.canConvert(QVariant::List)) {
1509         qWarning("Kit \"%s\" has a wrong environment value set.", qPrintable(k->displayName()));
1510         setEnvironmentChanges(k, Utils::EnvironmentItems());
1511     }
1512 }
1513 
addToBuildEnvironment(const Kit * k,Environment & env) const1514 void EnvironmentKitAspect::addToBuildEnvironment(const Kit *k, Environment &env) const
1515 {
1516     const QStringList values
1517             = Utils::transform(Utils::EnvironmentItem::toStringList(environmentChanges(k)),
1518                                [k](const QString &v) { return k->macroExpander()->expand(v); });
1519     env.modify(Utils::EnvironmentItem::fromStringList(values));
1520 }
1521 
addToRunEnvironment(const Kit * k,Environment & env) const1522 void EnvironmentKitAspect::addToRunEnvironment(const Kit *k, Environment &env) const
1523 {
1524     addToBuildEnvironment(k, env);
1525 }
1526 
createConfigWidget(Kit * k) const1527 KitAspectWidget *EnvironmentKitAspect::createConfigWidget(Kit *k) const
1528 {
1529     QTC_ASSERT(k, return nullptr);
1530     return new Internal::EnvironmentKitAspectWidget(k, this);
1531 }
1532 
toUserOutput(const Kit * k) const1533 KitAspect::ItemList EnvironmentKitAspect::toUserOutput(const Kit *k) const
1534 {
1535     return { qMakePair(tr("Environment"),
1536              Utils::EnvironmentItem::toStringList(environmentChanges(k)).join("<br>")) };
1537 }
1538 
id()1539 Utils::Id EnvironmentKitAspect::id()
1540 {
1541     return "PE.Profile.Environment";
1542 }
1543 
environmentChanges(const Kit * k)1544 Utils::EnvironmentItems EnvironmentKitAspect::environmentChanges(const Kit *k)
1545 {
1546      if (k)
1547          return Utils::EnvironmentItem::fromStringList(k->value(EnvironmentKitAspect::id()).toStringList());
1548      return Utils::EnvironmentItems();
1549 }
1550 
setEnvironmentChanges(Kit * k,const Utils::EnvironmentItems & changes)1551 void EnvironmentKitAspect::setEnvironmentChanges(Kit *k, const Utils::EnvironmentItems &changes)
1552 {
1553     if (k)
1554         k->setValue(EnvironmentKitAspect::id(), Utils::EnvironmentItem::toStringList(changes));
1555 }
1556 
1557 } // namespace ProjectExplorer
1558