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