forked from qt-creator/qt-creator
... and use in ToolchainKitAspect in addition to KitManager. Change-Id: I4c9221735a660697baab667877cc335ee0ee3e78 Reviewed-by: hjk <hjk@qt.io>
1636 lines
54 KiB
C++
1636 lines
54 KiB
C++
// Copyright (C) 2016 The Qt Company Ltd.
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
|
|
|
#include "kitaspects.h"
|
|
|
|
#include "abi.h"
|
|
#include "devicesupport/devicemanager.h"
|
|
#include "devicesupport/devicemanagermodel.h"
|
|
#include "devicesupport/idevicefactory.h"
|
|
#include "devicesupport/sshparameters.h"
|
|
#include "projectexplorerconstants.h"
|
|
#include "projectexplorertr.h"
|
|
#include "kit.h"
|
|
#include "toolchain.h"
|
|
#include "toolchainmanager.h"
|
|
|
|
#include <utils/algorithm.h>
|
|
#include <utils/elidinglabel.h>
|
|
#include <utils/environment.h>
|
|
#include <utils/environmentdialog.h>
|
|
#include <utils/guard.h>
|
|
#include <utils/guiutils.h>
|
|
#include <utils/layoutbuilder.h>
|
|
#include <utils/macroexpander.h>
|
|
#include <utils/pathchooser.h>
|
|
#include <utils/qtcassert.h>
|
|
#include <utils/variablechooser.h>
|
|
|
|
#include <QCheckBox>
|
|
#include <QComboBox>
|
|
#include <QFontMetrics>
|
|
#include <QGridLayout>
|
|
#include <QHash>
|
|
#include <QLabel>
|
|
#include <QPushButton>
|
|
#include <QVBoxLayout>
|
|
|
|
#include <utility>
|
|
|
|
using namespace Utils;
|
|
|
|
namespace ProjectExplorer {
|
|
|
|
// --------------------------------------------------------------------------
|
|
// SysRootKitAspect:
|
|
// --------------------------------------------------------------------------
|
|
|
|
namespace Internal {
|
|
class SysRootKitAspectImpl : public KitAspect
|
|
{
|
|
public:
|
|
SysRootKitAspectImpl(Kit *k, const KitAspectFactory *factory) : KitAspect(k, factory)
|
|
{
|
|
m_chooser = createSubWidget<PathChooser>();
|
|
m_chooser->setExpectedKind(PathChooser::ExistingDirectory);
|
|
m_chooser->setHistoryCompleter("PE.SysRoot.History");
|
|
m_chooser->setFilePath(SysRootKitAspect::sysRoot(k));
|
|
connect(m_chooser, &PathChooser::textChanged,
|
|
this, &SysRootKitAspectImpl::pathWasChanged);
|
|
}
|
|
|
|
~SysRootKitAspectImpl() override { delete m_chooser; }
|
|
|
|
private:
|
|
void makeReadOnly() override { m_chooser->setReadOnly(true); }
|
|
|
|
void addToInnerLayout(Layouting::Layout &builder) override
|
|
{
|
|
addMutableAction(m_chooser);
|
|
builder.addItem(Layouting::Span(2, m_chooser));
|
|
}
|
|
|
|
void refresh() override
|
|
{
|
|
if (!m_ignoreChanges.isLocked())
|
|
m_chooser->setFilePath(SysRootKitAspect::sysRoot(m_kit));
|
|
}
|
|
|
|
void pathWasChanged()
|
|
{
|
|
const GuardLocker locker(m_ignoreChanges);
|
|
SysRootKitAspect::setSysRoot(m_kit, m_chooser->filePath());
|
|
}
|
|
|
|
PathChooser *m_chooser;
|
|
Guard m_ignoreChanges;
|
|
};
|
|
} // namespace Internal
|
|
|
|
class SysRootKitAspectFactory : public KitAspectFactory
|
|
{
|
|
public:
|
|
SysRootKitAspectFactory();
|
|
|
|
Tasks validate(const Kit *k) const override;
|
|
KitAspect *createKitAspect(Kit *k) const override;
|
|
ItemList toUserOutput(const Kit *k) const override;
|
|
void addToMacroExpander(Kit *kit, MacroExpander *expander) const override;
|
|
};
|
|
|
|
SysRootKitAspectFactory::SysRootKitAspectFactory()
|
|
{
|
|
setId(SysRootKitAspect::id());
|
|
setDisplayName(Tr::tr("Sysroot"));
|
|
setDescription(Tr::tr("The root directory of the system image to use.<br>"
|
|
"Leave empty when building for the desktop."));
|
|
setPriority(27000);
|
|
}
|
|
|
|
Tasks SysRootKitAspectFactory::validate(const Kit *k) const
|
|
{
|
|
Tasks result;
|
|
const FilePath dir = SysRootKitAspect::sysRoot(k);
|
|
if (dir.isEmpty())
|
|
return result;
|
|
|
|
if (dir.startsWith("target:") || dir.startsWith("remote:"))
|
|
return result;
|
|
|
|
if (!dir.exists()) {
|
|
result << BuildSystemTask(Task::Warning,
|
|
Tr::tr("Sys Root \"%1\" does not exist in the file system.").arg(dir.toUserOutput()));
|
|
} else if (!dir.isDir()) {
|
|
result << BuildSystemTask(Task::Warning,
|
|
Tr::tr("Sys Root \"%1\" is not a directory.").arg(dir.toUserOutput()));
|
|
} else if (dir.dirEntries(QDir::AllEntries | QDir::NoDotAndDotDot).isEmpty()) {
|
|
result << BuildSystemTask(Task::Warning,
|
|
Tr::tr("Sys Root \"%1\" is empty.").arg(dir.toUserOutput()));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
KitAspect *SysRootKitAspectFactory::createKitAspect(Kit *k) const
|
|
{
|
|
QTC_ASSERT(k, return nullptr);
|
|
|
|
return new Internal::SysRootKitAspectImpl(k, this);
|
|
}
|
|
|
|
KitAspectFactory::ItemList SysRootKitAspectFactory::toUserOutput(const Kit *k) const
|
|
{
|
|
return {{Tr::tr("Sys Root"), SysRootKitAspect::sysRoot(k).toUserOutput()}};
|
|
}
|
|
|
|
void SysRootKitAspectFactory::addToMacroExpander(Kit *kit, MacroExpander *expander) const
|
|
{
|
|
QTC_ASSERT(kit, return);
|
|
|
|
expander->registerFileVariables("SysRoot", Tr::tr("Sys Root"), [kit] {
|
|
return SysRootKitAspect::sysRoot(kit);
|
|
});
|
|
}
|
|
|
|
Id SysRootKitAspect::id()
|
|
{
|
|
return "PE.Profile.SysRoot";
|
|
}
|
|
|
|
FilePath SysRootKitAspect::sysRoot(const Kit *k)
|
|
{
|
|
if (!k)
|
|
return {};
|
|
|
|
if (!k->value(SysRootKitAspect::id()).toString().isEmpty())
|
|
return FilePath::fromSettings(k->value(SysRootKitAspect::id()));
|
|
|
|
for (Toolchain *tc : ToolchainKitAspect::toolChains(k)) {
|
|
if (!tc->sysRoot().isEmpty())
|
|
return FilePath::fromString(tc->sysRoot());
|
|
}
|
|
return {};
|
|
}
|
|
|
|
void SysRootKitAspect::setSysRoot(Kit *k, const FilePath &v)
|
|
{
|
|
if (!k)
|
|
return;
|
|
|
|
for (Toolchain *tc : ToolchainKitAspect::toolChains(k)) {
|
|
if (!tc->sysRoot().isEmpty()) {
|
|
// It's the sysroot from toolchain, don't set it.
|
|
if (tc->sysRoot() == v.toString())
|
|
return;
|
|
|
|
// We've changed the default toolchain sysroot, set it.
|
|
break;
|
|
}
|
|
}
|
|
k->setValue(SysRootKitAspect::id(), v.toString());
|
|
}
|
|
|
|
const SysRootKitAspectFactory theSyRootKitAspectFactory;
|
|
|
|
// --------------------------------------------------------------------------
|
|
// ToolchainKitAspect:
|
|
// --------------------------------------------------------------------------
|
|
|
|
namespace Internal {
|
|
class ToolchainKitAspectImpl final : public KitAspect
|
|
{
|
|
public:
|
|
ToolchainKitAspectImpl(Kit *k, const KitAspectFactory *factory) : KitAspect(k, factory)
|
|
{
|
|
m_mainWidget = createSubWidget<QWidget>();
|
|
m_mainWidget->setContentsMargins(0, 0, 0, 0);
|
|
|
|
auto layout = new QGridLayout(m_mainWidget);
|
|
layout->setContentsMargins(0, 0, 0, 0);
|
|
layout->setColumnStretch(1, 2);
|
|
|
|
const QList<LanguageCategory> languageCategories = sorted(
|
|
ToolchainManager::languageCategories(),
|
|
[](const LanguageCategory &l1, const LanguageCategory &l2) {
|
|
return ToolchainManager::displayNameOfLanguageCategory(l1)
|
|
< ToolchainManager::displayNameOfLanguageCategory(l2);
|
|
});
|
|
QTC_ASSERT(!languageCategories.isEmpty(), return);
|
|
int row = 0;
|
|
for (const LanguageCategory &lc : std::as_const(languageCategories)) {
|
|
layout->addWidget(
|
|
new QLabel(ToolchainManager::displayNameOfLanguageCategory(lc) + ':'), row, 0);
|
|
auto cb = new QComboBox;
|
|
cb->setSizePolicy(QSizePolicy::Ignored, cb->sizePolicy().verticalPolicy());
|
|
cb->setToolTip(factory->description());
|
|
setWheelScrollingWithoutFocusBlocked(cb);
|
|
|
|
m_languageComboboxMap.insert(lc, cb);
|
|
layout->addWidget(cb, row, 1);
|
|
++row;
|
|
|
|
connect(cb, &QComboBox::currentIndexChanged, this, [this, lc](int idx) {
|
|
currentToolchainChanged(lc, idx);
|
|
});
|
|
}
|
|
|
|
refresh();
|
|
|
|
setManagingPage(Constants::TOOLCHAIN_SETTINGS_PAGE_ID);
|
|
}
|
|
|
|
~ToolchainKitAspectImpl() override
|
|
{
|
|
delete m_mainWidget;
|
|
}
|
|
|
|
private:
|
|
void addToInnerLayout(Layouting::Layout &builder) override
|
|
{
|
|
addMutableAction(m_mainWidget);
|
|
builder.addItem(m_mainWidget);
|
|
}
|
|
|
|
void refresh() override
|
|
{
|
|
IDeviceConstPtr device = BuildDeviceKitAspect::device(kit());
|
|
|
|
const GuardLocker locker(m_ignoreChanges);
|
|
for (auto it = m_languageComboboxMap.cbegin(); it != m_languageComboboxMap.cend(); ++it) {
|
|
const LanguageCategory lc = it.key();
|
|
const Toolchains ltcList = ToolchainManager::toolchains(
|
|
[lc](const Toolchain *tc) { return lc.contains(tc->language()); });
|
|
|
|
QComboBox *cb = *it;
|
|
cb->clear();
|
|
cb->addItem(Tr::tr("<No compiler>"), QByteArray());
|
|
|
|
const QList<Toolchain *> same = Utils::filtered(ltcList, [device](Toolchain *tc) {
|
|
return tc->compilerCommand().isSameDevice(device->rootPath());
|
|
});
|
|
const QList<Toolchain *> other = Utils::filtered(ltcList, [device](Toolchain *tc) {
|
|
return !tc->compilerCommand().isSameDevice(device->rootPath());
|
|
});
|
|
|
|
const QList<ToolchainBundle> sameBundles
|
|
= ToolchainBundle::collectBundles(same, ToolchainBundle::AutoRegister::On);
|
|
const QList<ToolchainBundle> otherBundles
|
|
= ToolchainBundle::collectBundles(other, ToolchainBundle::AutoRegister::On);
|
|
for (const ToolchainBundle &b : sameBundles)
|
|
cb->addItem(b.displayName(), b.bundleId().toSetting());
|
|
|
|
if (!sameBundles.isEmpty() && !otherBundles.isEmpty())
|
|
cb->insertSeparator(cb->count());
|
|
|
|
for (const ToolchainBundle &b : otherBundles)
|
|
cb->addItem(b.displayName(), b.bundleId().toSetting());
|
|
|
|
cb->setEnabled(cb->count() > 1 && !m_isReadOnly);
|
|
Id currentBundleId;
|
|
for (const Id lang : lc) {
|
|
Toolchain * const currentTc = ToolchainKitAspect::toolchain(m_kit, lang);
|
|
if (!currentTc)
|
|
continue;
|
|
for (const QList<ToolchainBundle> &bundles : {sameBundles, otherBundles})
|
|
for (const ToolchainBundle &b : bundles) {
|
|
if (b.bundleId() == currentTc->bundleId()) {
|
|
currentBundleId = b.bundleId();
|
|
break;
|
|
}
|
|
if (currentBundleId.isValid())
|
|
break;
|
|
}
|
|
if (currentBundleId.isValid())
|
|
break;
|
|
}
|
|
cb->setCurrentIndex(currentBundleId.isValid() ? indexOf(cb, currentBundleId) : -1);
|
|
}
|
|
}
|
|
|
|
void makeReadOnly() override
|
|
{
|
|
m_isReadOnly = true;
|
|
for (QComboBox *cb : std::as_const(m_languageComboboxMap))
|
|
cb->setEnabled(false);
|
|
}
|
|
|
|
void currentToolchainChanged(const LanguageCategory &languageCategory, int idx)
|
|
{
|
|
if (m_ignoreChanges.isLocked() || idx < 0)
|
|
return;
|
|
|
|
const Id bundleId = Id::fromSetting(
|
|
m_languageComboboxMap.value(languageCategory)->itemData(idx));
|
|
const Toolchains bundleTcs = ToolchainManager::toolchains(
|
|
[bundleId](const Toolchain *tc) { return tc->bundleId() == bundleId; });
|
|
for (const Id lang : languageCategory) {
|
|
Toolchain *const tc = Utils::findOrDefault(bundleTcs, [lang](const Toolchain *tc) {
|
|
return tc->language() == lang;
|
|
});
|
|
if (tc)
|
|
ToolchainKitAspect::setToolchain(m_kit, tc);
|
|
else
|
|
ToolchainKitAspect::clearToolchain(m_kit, lang);
|
|
}
|
|
}
|
|
|
|
int indexOf(QComboBox *cb, Id bundleId)
|
|
{
|
|
for (int i = 0; i < cb->count(); ++i) {
|
|
if (bundleId.toSetting() == cb->itemData(i))
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
QWidget *m_mainWidget = nullptr;
|
|
QHash<LanguageCategory, QComboBox *> m_languageComboboxMap;
|
|
Guard m_ignoreChanges;
|
|
bool m_isReadOnly = false;
|
|
};
|
|
} // namespace Internal
|
|
|
|
class ToolchainKitAspectFactory : public KitAspectFactory
|
|
{
|
|
public:
|
|
ToolchainKitAspectFactory();
|
|
|
|
private:
|
|
Tasks validate(const Kit *k) const override;
|
|
void fix(Kit *k) override;
|
|
void setup(Kit *k) override;
|
|
|
|
KitAspect *createKitAspect(Kit *k) const override;
|
|
|
|
QString displayNamePostfix(const Kit *k) const override;
|
|
|
|
ItemList toUserOutput(const Kit *k) const override;
|
|
|
|
void addToBuildEnvironment(const Kit *k, Environment &env) const override;
|
|
void addToRunEnvironment(const Kit *, Environment &) const override {}
|
|
|
|
void addToMacroExpander(Kit *kit, MacroExpander *expander) const override;
|
|
QList<OutputLineParser *> createOutputParsers(const Kit *k) const override;
|
|
QSet<Id> availableFeatures(const Kit *k) const override;
|
|
|
|
void onKitsLoaded() override;
|
|
|
|
void toolChainUpdated(Toolchain *tc);
|
|
void toolChainsDeregistered();
|
|
};
|
|
|
|
ToolchainKitAspectFactory::ToolchainKitAspectFactory()
|
|
{
|
|
setId(ToolchainKitAspect::id());
|
|
setDisplayName(Tr::tr("Compiler"));
|
|
setDescription(Tr::tr("The compiler to use for building.<br>"
|
|
"Make sure the compiler will produce binaries compatible "
|
|
"with the target device, Qt version and other libraries used."));
|
|
setPriority(30000);
|
|
}
|
|
|
|
Tasks ToolchainKitAspectFactory::validate(const Kit *k) const
|
|
{
|
|
Tasks result;
|
|
|
|
const QList<Toolchain*> tcList = ToolchainKitAspect::toolChains(k);
|
|
if (tcList.isEmpty()) {
|
|
result << BuildSystemTask(Task::Warning, ToolchainKitAspect::msgNoToolchainInTarget());
|
|
} else {
|
|
QSet<Abi> targetAbis;
|
|
for (const Toolchain *tc : tcList) {
|
|
targetAbis.insert(tc->targetAbi());
|
|
result << tc->validateKit(k);
|
|
}
|
|
if (targetAbis.count() != 1) {
|
|
result << BuildSystemTask(Task::Error,
|
|
Tr::tr("Compilers produce code for different ABIs: %1")
|
|
.arg(Utils::transform<QList>(targetAbis, &Abi::toString).join(", ")));
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void ToolchainKitAspectFactory::fix(Kit *k)
|
|
{
|
|
QTC_ASSERT(ToolchainManager::isLoaded(), return);
|
|
const QList<Id> languages = ToolchainManager::allLanguages();
|
|
for (const Id l : languages) {
|
|
const QByteArray tcId = ToolchainKitAspect::toolchainId(k, l);
|
|
if (!tcId.isEmpty() && !ToolchainManager::findToolchain(tcId)) {
|
|
qWarning("Tool chain set up in kit \"%s\" for \"%s\" not found.",
|
|
qPrintable(k->displayName()),
|
|
qPrintable(ToolchainManager::displayNameOfLanguageId(l)));
|
|
ToolchainKitAspect::clearToolchain(k, l); // make sure to clear out no longer known tool chains
|
|
}
|
|
}
|
|
}
|
|
|
|
static Id findLanguage(const QString &ls)
|
|
{
|
|
QString lsUpper = ls.toUpper();
|
|
return Utils::findOrDefault(ToolchainManager::allLanguages(),
|
|
[lsUpper](Id l) { return lsUpper == l.toString().toUpper(); });
|
|
}
|
|
|
|
using LanguageAndAbi = std::pair<Id, Abi>;
|
|
using LanguagesAndAbis = QList<LanguageAndAbi>;
|
|
|
|
static void setToolchainsFromAbis(Kit *k, const LanguagesAndAbis &abisByLanguage)
|
|
{
|
|
if (abisByLanguage.isEmpty())
|
|
return;
|
|
|
|
// First transform languages into categories, so we can work on the bundle level.
|
|
// Obviously, we assume that the caller does not specify different ABIs for
|
|
// languages from the same category.
|
|
const QList<LanguageCategory> allCategories = ToolchainManager::languageCategories();
|
|
QHash<LanguageCategory, Abi> abisByCategory;
|
|
for (const LanguageAndAbi &langAndAbi : abisByLanguage) {
|
|
const auto category
|
|
= Utils::findOrDefault(allCategories, [&langAndAbi](const LanguageCategory &cat) {
|
|
return cat.contains(langAndAbi.first);
|
|
});
|
|
QTC_ASSERT(!category.isEmpty(), continue);
|
|
abisByCategory.insert(category, langAndAbi.second);
|
|
}
|
|
|
|
// Get bundles.
|
|
const QList<ToolchainBundle> bundles = ToolchainBundle::collectBundles(
|
|
ToolchainBundle::AutoRegister::On);
|
|
|
|
// Set a matching bundle for each LanguageCategory/Abi pair, if possible.
|
|
for (auto it = abisByCategory.cbegin(); it != abisByCategory.cend(); ++it) {
|
|
const QList<ToolchainBundle> matchingBundles
|
|
= Utils::filtered(bundles, [&it](const ToolchainBundle &b) {
|
|
return b.factory()->languageCategory() == it.key() && b.targetAbi() == it.value();
|
|
});
|
|
|
|
if (matchingBundles.isEmpty()) {
|
|
for (const Id language : it.key())
|
|
ToolchainKitAspect::clearToolchain(k, language);
|
|
continue;
|
|
}
|
|
|
|
const auto bestBundle
|
|
= std::min_element(bundles.begin(), bundles.end(), &ToolchainManager::isBetterToolchain);
|
|
ToolchainKitAspect::setBundle(k, *bestBundle);
|
|
}
|
|
}
|
|
|
|
static void setMissingToolchainsToHostAbi(Kit *k, const QList<Id> &languageBlacklist)
|
|
{
|
|
LanguagesAndAbis abisByLanguage;
|
|
for (const Id lang : ToolchainManager::allLanguages()) {
|
|
if (languageBlacklist.contains(lang) || ToolchainKitAspect::toolchain(k, lang))
|
|
continue;
|
|
abisByLanguage.emplaceBack(lang, Abi::hostAbi());
|
|
}
|
|
setToolchainsFromAbis(k, abisByLanguage);
|
|
}
|
|
|
|
static void setupForSdkKit(Kit *k)
|
|
{
|
|
const Store value = storeFromVariant(k->value(ToolchainKitAspect::id()));
|
|
bool lockToolchains = !value.isEmpty();
|
|
|
|
// The installer provides two kinds of entries for toolchains:
|
|
// a) An actual toolchain id, for e.g. Boot2Qt where the installer ships the toolchains.
|
|
// b) An ABI string, for Desktop Qt. In this case, it is our responsibility to find
|
|
// a matching toolchain on the host system.
|
|
LanguagesAndAbis abisByLanguage;
|
|
for (auto i = value.constBegin(); i != value.constEnd(); ++i) {
|
|
const Id lang = findLanguage(stringFromKey(i.key()));
|
|
|
|
if (!lang.isValid()) {
|
|
lockToolchains = false;
|
|
continue;
|
|
}
|
|
|
|
const QByteArray id = i.value().toByteArray();
|
|
if (ToolchainManager::findToolchain(id))
|
|
continue;
|
|
|
|
// No toolchain with this id exists. Check whether it's an ABI string.
|
|
lockToolchains = false;
|
|
const Abi abi = Abi::fromString(QString::fromUtf8(id));
|
|
if (!abi.isValid())
|
|
continue;
|
|
|
|
abisByLanguage.emplaceBack(lang, abi);
|
|
}
|
|
setToolchainsFromAbis(k, abisByLanguage);
|
|
setMissingToolchainsToHostAbi(k, Utils::transform(abisByLanguage, &LanguageAndAbi::first));
|
|
|
|
k->setSticky(ToolchainKitAspect::id(), lockToolchains);
|
|
}
|
|
|
|
static void setupForNonSdkKit(Kit *k)
|
|
{
|
|
setMissingToolchainsToHostAbi(k, {});
|
|
k->setSticky(ToolchainKitAspect::id(), false);
|
|
}
|
|
|
|
void ToolchainKitAspectFactory::setup(Kit *k)
|
|
{
|
|
QTC_ASSERT(ToolchainManager::isLoaded(), return);
|
|
QTC_ASSERT(k, return);
|
|
|
|
if (k->isSdkProvided())
|
|
setupForSdkKit(k);
|
|
else
|
|
setupForNonSdkKit(k);
|
|
}
|
|
|
|
KitAspect *ToolchainKitAspectFactory::createKitAspect(Kit *k) const
|
|
{
|
|
QTC_ASSERT(k, return nullptr);
|
|
return new Internal::ToolchainKitAspectImpl(k, this);
|
|
}
|
|
|
|
QString ToolchainKitAspectFactory::displayNamePostfix(const Kit *k) const
|
|
{
|
|
Toolchain *tc = ToolchainKitAspect::cxxToolchain(k);
|
|
return tc ? tc->displayName() : QString();
|
|
}
|
|
|
|
KitAspectFactory::ItemList ToolchainKitAspectFactory::toUserOutput(const Kit *k) const
|
|
{
|
|
Toolchain *tc = ToolchainKitAspect::cxxToolchain(k);
|
|
return {{Tr::tr("Compiler"), tc ? tc->displayName() : Tr::tr("None")}};
|
|
}
|
|
|
|
void ToolchainKitAspectFactory::addToBuildEnvironment(const Kit *k, Environment &env) const
|
|
{
|
|
Toolchain *tc = ToolchainKitAspect::cxxToolchain(k);
|
|
if (tc)
|
|
tc->addToEnvironment(env);
|
|
}
|
|
|
|
void ToolchainKitAspectFactory::addToMacroExpander(Kit *kit, MacroExpander *expander) const
|
|
{
|
|
QTC_ASSERT(kit, return);
|
|
|
|
// Compatibility with Qt Creator < 4.2:
|
|
expander->registerVariable("Compiler:Name", Tr::tr("Compiler"),
|
|
[kit] {
|
|
const Toolchain *tc = ToolchainKitAspect::cxxToolchain(kit);
|
|
return tc ? tc->displayName() : Tr::tr("None");
|
|
});
|
|
|
|
expander->registerVariable("Compiler:Executable", Tr::tr("Path to the compiler executable"),
|
|
[kit] {
|
|
const Toolchain *tc = ToolchainKitAspect::cxxToolchain(kit);
|
|
return tc ? tc->compilerCommand().path() : QString();
|
|
});
|
|
|
|
// After 4.2
|
|
expander->registerPrefix("Compiler:Name", Tr::tr("Compiler for different languages"),
|
|
[kit](const QString &ls) {
|
|
const Toolchain *tc = ToolchainKitAspect::toolchain(kit, findLanguage(ls));
|
|
return tc ? tc->displayName() : Tr::tr("None");
|
|
});
|
|
expander->registerPrefix("Compiler:Executable", Tr::tr("Compiler executable for different languages"),
|
|
[kit](const QString &ls) {
|
|
const Toolchain *tc = ToolchainKitAspect::toolchain(kit, findLanguage(ls));
|
|
return tc ? tc->compilerCommand().path() : QString();
|
|
});
|
|
}
|
|
|
|
QList<OutputLineParser *> ToolchainKitAspectFactory::createOutputParsers(const Kit *k) const
|
|
{
|
|
for (const Id langId : {Constants::CXX_LANGUAGE_ID, Constants::C_LANGUAGE_ID}) {
|
|
if (const Toolchain * const tc = ToolchainKitAspect::toolchain(k, langId))
|
|
return tc->createOutputParsers();
|
|
}
|
|
return {};
|
|
}
|
|
|
|
QSet<Id> ToolchainKitAspectFactory::availableFeatures(const Kit *k) const
|
|
{
|
|
QSet<Id> result;
|
|
for (Toolchain *tc : ToolchainKitAspect::toolChains(k))
|
|
result.insert(tc->typeId().withPrefix("ToolChain."));
|
|
return result;
|
|
}
|
|
|
|
Id ToolchainKitAspect::id()
|
|
{
|
|
// "PE.Profile.ToolChain" until 4.2
|
|
// "PE.Profile.ToolChains" temporarily before 4.3 (May 2017)
|
|
return "PE.Profile.ToolChainsV3";
|
|
}
|
|
|
|
QByteArray ToolchainKitAspect::toolchainId(const Kit *k, Id language)
|
|
{
|
|
QTC_ASSERT(ToolchainManager::isLoaded(), return nullptr);
|
|
if (!k)
|
|
return {};
|
|
Store value = storeFromVariant(k->value(ToolchainKitAspect::id()));
|
|
return value.value(language.toKey(), QByteArray()).toByteArray();
|
|
}
|
|
|
|
Toolchain *ToolchainKitAspect::toolchain(const Kit *k, Id language)
|
|
{
|
|
return ToolchainManager::findToolchain(toolchainId(k, language));
|
|
}
|
|
|
|
Toolchain *ToolchainKitAspect::cToolchain(const Kit *k)
|
|
{
|
|
return ToolchainManager::findToolchain(toolchainId(k, ProjectExplorer::Constants::C_LANGUAGE_ID));
|
|
}
|
|
|
|
Toolchain *ToolchainKitAspect::cxxToolchain(const Kit *k)
|
|
{
|
|
return ToolchainManager::findToolchain(toolchainId(k, ProjectExplorer::Constants::CXX_LANGUAGE_ID));
|
|
}
|
|
|
|
|
|
QList<Toolchain *> ToolchainKitAspect::toolChains(const Kit *k)
|
|
{
|
|
QTC_ASSERT(k, return {});
|
|
|
|
const Store value = storeFromVariant(k->value(ToolchainKitAspect::id()));
|
|
const QList<Toolchain *> tcList
|
|
= transform<QList>(ToolchainManager::allLanguages(), [&value](Id l) {
|
|
return ToolchainManager::findToolchain(value.value(l.toKey()).toByteArray());
|
|
});
|
|
return filtered(tcList, [](Toolchain *tc) { return tc; });
|
|
}
|
|
|
|
void ToolchainKitAspect::setToolchain(Kit *k, Toolchain *tc)
|
|
{
|
|
QTC_ASSERT(tc, return);
|
|
QTC_ASSERT(k, return);
|
|
Store result = storeFromVariant(k->value(ToolchainKitAspect::id()));
|
|
result.insert(tc->language().toKey(), tc->id());
|
|
|
|
k->setValue(id(), variantFromStore(result));
|
|
}
|
|
|
|
void ToolchainKitAspect::setBundle(Kit *k, const ToolchainBundle &bundle)
|
|
{
|
|
bundle.forEach<Toolchain>([k](Toolchain &tc) {
|
|
setToolchain(k, &tc);
|
|
});
|
|
}
|
|
|
|
void ToolchainKitAspect::clearToolchain(Kit *k, Id language)
|
|
{
|
|
QTC_ASSERT(language.isValid(), return);
|
|
QTC_ASSERT(k, return);
|
|
|
|
Store result = storeFromVariant(k->value(ToolchainKitAspect::id()));
|
|
result.insert(language.toKey(), QByteArray());
|
|
k->setValue(id(), variantFromStore(result));
|
|
}
|
|
|
|
Abi ToolchainKitAspect::targetAbi(const Kit *k)
|
|
{
|
|
const QList<Toolchain *> tcList = toolChains(k);
|
|
// Find the best possible ABI for all the tool chains...
|
|
Abi cxxAbi;
|
|
QHash<Abi, int> abiCount;
|
|
for (Toolchain *tc : tcList) {
|
|
Abi ta = tc->targetAbi();
|
|
if (tc->language() == Id(Constants::CXX_LANGUAGE_ID))
|
|
cxxAbi = tc->targetAbi();
|
|
abiCount[ta] = (abiCount.contains(ta) ? abiCount[ta] + 1 : 1);
|
|
}
|
|
QVector<Abi> candidates;
|
|
int count = -1;
|
|
candidates.reserve(tcList.count());
|
|
for (auto i = abiCount.begin(); i != abiCount.end(); ++i) {
|
|
if (i.value() > count) {
|
|
candidates.clear();
|
|
candidates.append(i.key());
|
|
count = i.value();
|
|
} else if (i.value() == count) {
|
|
candidates.append(i.key());
|
|
}
|
|
}
|
|
|
|
// Found a good candidate:
|
|
if (candidates.isEmpty())
|
|
return Abi::hostAbi();
|
|
if (candidates.contains(cxxAbi)) // Use Cxx compiler as a tie breaker
|
|
return cxxAbi;
|
|
return candidates.at(0); // Use basically a random Abi...
|
|
}
|
|
|
|
QString ToolchainKitAspect::msgNoToolchainInTarget()
|
|
{
|
|
return Tr::tr("No compiler set in kit.");
|
|
}
|
|
|
|
void ToolchainKitAspectFactory::onKitsLoaded()
|
|
{
|
|
for (Kit *k : KitManager::kits())
|
|
fix(k);
|
|
|
|
connect(ToolchainManager::instance(), &ToolchainManager::toolchainsDeregistered,
|
|
this, &ToolchainKitAspectFactory::toolChainsDeregistered);
|
|
connect(ToolchainManager::instance(), &ToolchainManager::toolchainUpdated,
|
|
this, &ToolchainKitAspectFactory::toolChainUpdated);
|
|
}
|
|
|
|
void ToolchainKitAspectFactory::toolChainUpdated(Toolchain *tc)
|
|
{
|
|
for (Kit *k : KitManager::kits()) {
|
|
if (ToolchainKitAspect::toolchain(k, tc->language()) == tc)
|
|
notifyAboutUpdate(k);
|
|
}
|
|
}
|
|
|
|
void ToolchainKitAspectFactory::toolChainsDeregistered()
|
|
{
|
|
for (Kit *k : KitManager::kits())
|
|
fix(k);
|
|
}
|
|
|
|
const ToolchainKitAspectFactory thsToolChainKitAspectFactory;
|
|
|
|
// --------------------------------------------------------------------------
|
|
// DeviceTypeKitAspect:
|
|
// --------------------------------------------------------------------------
|
|
namespace Internal {
|
|
class DeviceTypeKitAspectImpl final : public KitAspect
|
|
{
|
|
public:
|
|
DeviceTypeKitAspectImpl(Kit *workingCopy, const KitAspectFactory *factory)
|
|
: KitAspect(workingCopy, factory), m_comboBox(createSubWidget<QComboBox>())
|
|
{
|
|
for (IDeviceFactory *factory : IDeviceFactory::allDeviceFactories())
|
|
m_comboBox->addItem(factory->displayName(), factory->deviceType().toSetting());
|
|
m_comboBox->setToolTip(factory->description());
|
|
refresh();
|
|
connect(m_comboBox, &QComboBox::currentIndexChanged,
|
|
this, &DeviceTypeKitAspectImpl::currentTypeChanged);
|
|
}
|
|
|
|
~DeviceTypeKitAspectImpl() override { delete m_comboBox; }
|
|
|
|
private:
|
|
void addToInnerLayout(Layouting::Layout &builder) override
|
|
{
|
|
addMutableAction(m_comboBox);
|
|
builder.addItem(m_comboBox);
|
|
}
|
|
|
|
void makeReadOnly() override { m_comboBox->setEnabled(false); }
|
|
|
|
void refresh() override
|
|
{
|
|
Id devType = DeviceTypeKitAspect::deviceTypeId(m_kit);
|
|
if (!devType.isValid())
|
|
m_comboBox->setCurrentIndex(-1);
|
|
for (int i = 0; i < m_comboBox->count(); ++i) {
|
|
if (m_comboBox->itemData(i) == devType.toSetting()) {
|
|
m_comboBox->setCurrentIndex(i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void currentTypeChanged(int idx)
|
|
{
|
|
Id type = idx < 0 ? Id() : Id::fromSetting(m_comboBox->itemData(idx));
|
|
DeviceTypeKitAspect::setDeviceTypeId(m_kit, type);
|
|
}
|
|
|
|
QComboBox *m_comboBox;
|
|
};
|
|
} // namespace Internal
|
|
|
|
class DeviceTypeKitAspectFactory : public KitAspectFactory
|
|
{
|
|
public:
|
|
DeviceTypeKitAspectFactory();
|
|
|
|
void setup(Kit *k) override;
|
|
Tasks validate(const Kit *k) const override;
|
|
KitAspect *createKitAspect(Kit *k) const override;
|
|
ItemList toUserOutput(const Kit *k) const override;
|
|
|
|
QSet<Id> supportedPlatforms(const Kit *k) const override;
|
|
QSet<Id> availableFeatures(const Kit *k) const override;
|
|
};
|
|
|
|
DeviceTypeKitAspectFactory::DeviceTypeKitAspectFactory()
|
|
{
|
|
setId(DeviceTypeKitAspect::id());
|
|
setDisplayName(Tr::tr("Run device type"));
|
|
setDescription(Tr::tr("The type of device to run applications on."));
|
|
setPriority(33000);
|
|
makeEssential();
|
|
}
|
|
|
|
void DeviceTypeKitAspectFactory::setup(Kit *k)
|
|
{
|
|
if (k && !k->hasValue(id()))
|
|
k->setValue(id(), QByteArray(Constants::DESKTOP_DEVICE_TYPE));
|
|
}
|
|
|
|
Tasks DeviceTypeKitAspectFactory::validate(const Kit *k) const
|
|
{
|
|
Q_UNUSED(k)
|
|
return {};
|
|
}
|
|
|
|
KitAspect *DeviceTypeKitAspectFactory::createKitAspect(Kit *k) const
|
|
{
|
|
QTC_ASSERT(k, return nullptr);
|
|
return new Internal::DeviceTypeKitAspectImpl(k, this);
|
|
}
|
|
|
|
KitAspectFactory::ItemList DeviceTypeKitAspectFactory::toUserOutput(const Kit *k) const
|
|
{
|
|
QTC_ASSERT(k, return {});
|
|
Id type = DeviceTypeKitAspect::deviceTypeId(k);
|
|
QString typeDisplayName = Tr::tr("Unknown device type");
|
|
if (type.isValid()) {
|
|
if (IDeviceFactory *factory = IDeviceFactory::find(type))
|
|
typeDisplayName = factory->displayName();
|
|
}
|
|
return {{Tr::tr("Device type"), typeDisplayName}};
|
|
}
|
|
|
|
const Id DeviceTypeKitAspect::id()
|
|
{
|
|
return "PE.Profile.DeviceType";
|
|
}
|
|
|
|
const Id DeviceTypeKitAspect::deviceTypeId(const Kit *k)
|
|
{
|
|
return k ? Id::fromSetting(k->value(DeviceTypeKitAspect::id())) : Id();
|
|
}
|
|
|
|
void DeviceTypeKitAspect::setDeviceTypeId(Kit *k, Id type)
|
|
{
|
|
QTC_ASSERT(k, return);
|
|
k->setValue(DeviceTypeKitAspect::id(), type.toSetting());
|
|
}
|
|
|
|
QSet<Id> DeviceTypeKitAspectFactory::supportedPlatforms(const Kit *k) const
|
|
{
|
|
return {DeviceTypeKitAspect::deviceTypeId(k)};
|
|
}
|
|
|
|
QSet<Id> DeviceTypeKitAspectFactory::availableFeatures(const Kit *k) const
|
|
{
|
|
Id id = DeviceTypeKitAspect::deviceTypeId(k);
|
|
if (id.isValid())
|
|
return {id.withPrefix("DeviceType.")};
|
|
return {};
|
|
}
|
|
|
|
const DeviceTypeKitAspectFactory theDeviceTypeKitAspectFactory;
|
|
|
|
// --------------------------------------------------------------------------
|
|
// DeviceKitAspect:
|
|
// --------------------------------------------------------------------------
|
|
namespace Internal {
|
|
class DeviceKitAspectImpl final : public KitAspect
|
|
{
|
|
public:
|
|
DeviceKitAspectImpl(Kit *workingCopy, const KitAspectFactory *factory)
|
|
: KitAspect(workingCopy, factory),
|
|
m_comboBox(createSubWidget<QComboBox>()),
|
|
m_model(new DeviceManagerModel(DeviceManager::instance()))
|
|
{
|
|
setManagingPage(Constants::DEVICE_SETTINGS_PAGE_ID);
|
|
m_comboBox->setSizePolicy(QSizePolicy::Preferred,
|
|
m_comboBox->sizePolicy().verticalPolicy());
|
|
m_comboBox->setModel(m_model);
|
|
m_comboBox->setMinimumContentsLength(16); // Don't stretch too much for Kit Page
|
|
refresh();
|
|
m_comboBox->setToolTip(factory->description());
|
|
|
|
connect(m_model, &QAbstractItemModel::modelAboutToBeReset,
|
|
this, &DeviceKitAspectImpl::modelAboutToReset);
|
|
connect(m_model, &QAbstractItemModel::modelReset,
|
|
this, &DeviceKitAspectImpl::modelReset);
|
|
connect(m_comboBox, &QComboBox::currentIndexChanged,
|
|
this, &DeviceKitAspectImpl::currentDeviceChanged);
|
|
}
|
|
|
|
~DeviceKitAspectImpl() override
|
|
{
|
|
delete m_comboBox;
|
|
delete m_model;
|
|
}
|
|
|
|
private:
|
|
void addToInnerLayout(Layouting::Layout &builder) override
|
|
{
|
|
addMutableAction(m_comboBox);
|
|
builder.addItem(m_comboBox);
|
|
}
|
|
|
|
void makeReadOnly() override { m_comboBox->setEnabled(false); }
|
|
|
|
Id settingsPageItemToPreselect() const override { return DeviceKitAspect::deviceId(m_kit); }
|
|
|
|
void refresh() override
|
|
{
|
|
m_model->setTypeFilter(DeviceTypeKitAspect::deviceTypeId(m_kit));
|
|
m_comboBox->setCurrentIndex(m_model->indexOf(DeviceKitAspect::device(m_kit)));
|
|
}
|
|
|
|
void modelAboutToReset()
|
|
{
|
|
m_selectedId = m_model->deviceId(m_comboBox->currentIndex());
|
|
m_ignoreChanges.lock();
|
|
}
|
|
|
|
void modelReset()
|
|
{
|
|
m_comboBox->setCurrentIndex(m_model->indexForId(m_selectedId));
|
|
m_ignoreChanges.unlock();
|
|
}
|
|
|
|
void currentDeviceChanged()
|
|
{
|
|
if (m_ignoreChanges.isLocked())
|
|
return;
|
|
DeviceKitAspect::setDeviceId(m_kit, m_model->deviceId(m_comboBox->currentIndex()));
|
|
}
|
|
|
|
Guard m_ignoreChanges;
|
|
QComboBox *m_comboBox;
|
|
DeviceManagerModel *m_model;
|
|
Id m_selectedId;
|
|
};
|
|
} // namespace Internal
|
|
|
|
class DeviceKitAspectFactory : public KitAspectFactory
|
|
{
|
|
public:
|
|
DeviceKitAspectFactory();
|
|
|
|
private:
|
|
Tasks validate(const Kit *k) const override;
|
|
void fix(Kit *k) override;
|
|
void setup(Kit *k) override;
|
|
|
|
KitAspect *createKitAspect(Kit *k) const override;
|
|
|
|
QString displayNamePostfix(const Kit *k) const override;
|
|
|
|
ItemList toUserOutput(const Kit *k) const override;
|
|
|
|
void addToMacroExpander(Kit *kit, MacroExpander *expander) const override;
|
|
|
|
QVariant defaultValue(const Kit *k) const;
|
|
|
|
void onKitsLoaded() override;
|
|
void deviceUpdated(Id dataId);
|
|
void devicesChanged();
|
|
void kitUpdated(Kit *k);
|
|
};
|
|
|
|
DeviceKitAspectFactory::DeviceKitAspectFactory()
|
|
{
|
|
setId(DeviceKitAspect::id());
|
|
setDisplayName(Tr::tr("Run device"));
|
|
setDescription(Tr::tr("The device to run the applications on."));
|
|
setPriority(32000);
|
|
}
|
|
|
|
QVariant DeviceKitAspectFactory::defaultValue(const Kit *k) const
|
|
{
|
|
Id type = DeviceTypeKitAspect::deviceTypeId(k);
|
|
// Use default device if that is compatible:
|
|
IDevice::ConstPtr dev = DeviceManager::instance()->defaultDevice(type);
|
|
if (dev && dev->isCompatibleWith(k))
|
|
return dev->id().toString();
|
|
// Use any other device that is compatible:
|
|
for (int i = 0; i < DeviceManager::instance()->deviceCount(); ++i) {
|
|
dev = DeviceManager::instance()->deviceAt(i);
|
|
if (dev && dev->isCompatibleWith(k))
|
|
return dev->id().toString();
|
|
}
|
|
// Fail: No device set up.
|
|
return {};
|
|
}
|
|
|
|
Tasks DeviceKitAspectFactory::validate(const Kit *k) const
|
|
{
|
|
IDevice::ConstPtr dev = DeviceKitAspect::device(k);
|
|
Tasks result;
|
|
if (!dev)
|
|
result.append(BuildSystemTask(Task::Warning, Tr::tr("No device set.")));
|
|
else if (!dev->isCompatibleWith(k))
|
|
result.append(BuildSystemTask(Task::Error, Tr::tr("Device is incompatible with this kit.")));
|
|
|
|
if (dev)
|
|
result.append(dev->validate());
|
|
|
|
return result;
|
|
}
|
|
|
|
void DeviceKitAspectFactory::fix(Kit *k)
|
|
{
|
|
IDevice::ConstPtr dev = DeviceKitAspect::device(k);
|
|
if (dev && !dev->isCompatibleWith(k)) {
|
|
qWarning("Device is no longer compatible with kit \"%s\", removing it.",
|
|
qPrintable(k->displayName()));
|
|
DeviceKitAspect::setDeviceId(k, Id());
|
|
}
|
|
}
|
|
|
|
void DeviceKitAspectFactory::setup(Kit *k)
|
|
{
|
|
QTC_ASSERT(DeviceManager::instance()->isLoaded(), return);
|
|
IDevice::ConstPtr dev = DeviceKitAspect::device(k);
|
|
if (dev && dev->isCompatibleWith(k))
|
|
return;
|
|
|
|
DeviceKitAspect::setDeviceId(k, Id::fromSetting(defaultValue(k)));
|
|
}
|
|
|
|
KitAspect *DeviceKitAspectFactory::createKitAspect(Kit *k) const
|
|
{
|
|
QTC_ASSERT(k, return nullptr);
|
|
return new Internal::DeviceKitAspectImpl(k, this);
|
|
}
|
|
|
|
QString DeviceKitAspectFactory::displayNamePostfix(const Kit *k) const
|
|
{
|
|
IDevice::ConstPtr dev = DeviceKitAspect::device(k);
|
|
return dev ? dev->displayName() : QString();
|
|
}
|
|
|
|
KitAspectFactory::ItemList DeviceKitAspectFactory::toUserOutput(const Kit *k) const
|
|
{
|
|
IDevice::ConstPtr dev = DeviceKitAspect::device(k);
|
|
return {{Tr::tr("Device"), dev ? dev->displayName() : Tr::tr("Unconfigured") }};
|
|
}
|
|
|
|
void DeviceKitAspectFactory::addToMacroExpander(Kit *kit, MacroExpander *expander) const
|
|
{
|
|
QTC_ASSERT(kit, return);
|
|
expander->registerVariable("Device:HostAddress", Tr::tr("Host address"), [kit] {
|
|
const IDevice::ConstPtr device = DeviceKitAspect::device(kit);
|
|
return device ? device->sshParameters().host() : QString();
|
|
});
|
|
expander->registerVariable("Device:SshPort", Tr::tr("SSH port"), [kit] {
|
|
const IDevice::ConstPtr device = DeviceKitAspect::device(kit);
|
|
return device ? QString::number(device->sshParameters().port()) : QString();
|
|
});
|
|
expander->registerVariable("Device:UserName", Tr::tr("User name"), [kit] {
|
|
const IDevice::ConstPtr device = DeviceKitAspect::device(kit);
|
|
return device ? device->sshParameters().userName() : QString();
|
|
});
|
|
expander->registerVariable("Device:KeyFile", Tr::tr("Private key file"), [kit] {
|
|
const IDevice::ConstPtr device = DeviceKitAspect::device(kit);
|
|
return device ? device->sshParameters().privateKeyFile.toString() : QString();
|
|
});
|
|
expander->registerVariable("Device:Name", Tr::tr("Device name"), [kit] {
|
|
const IDevice::ConstPtr device = DeviceKitAspect::device(kit);
|
|
return device ? device->displayName() : QString();
|
|
});
|
|
expander->registerFileVariables("Device::Root", Tr::tr("Device root directory"), [kit] {
|
|
const IDevice::ConstPtr device = DeviceKitAspect::device(kit);
|
|
return device ? device->rootPath() : FilePath{};
|
|
});
|
|
}
|
|
|
|
Id DeviceKitAspect::id()
|
|
{
|
|
return "PE.Profile.Device";
|
|
}
|
|
|
|
IDevice::ConstPtr DeviceKitAspect::device(const Kit *k)
|
|
{
|
|
QTC_ASSERT(DeviceManager::instance()->isLoaded(), return IDevice::ConstPtr());
|
|
return DeviceManager::instance()->find(deviceId(k));
|
|
}
|
|
|
|
Id DeviceKitAspect::deviceId(const Kit *k)
|
|
{
|
|
return k ? Id::fromSetting(k->value(DeviceKitAspect::id())) : Id();
|
|
}
|
|
|
|
void DeviceKitAspect::setDevice(Kit *k, IDevice::ConstPtr dev)
|
|
{
|
|
setDeviceId(k, dev ? dev->id() : Id());
|
|
}
|
|
|
|
void DeviceKitAspect::setDeviceId(Kit *k, Id id)
|
|
{
|
|
QTC_ASSERT(k, return);
|
|
k->setValue(DeviceKitAspect::id(), id.toSetting());
|
|
}
|
|
|
|
FilePath DeviceKitAspect::deviceFilePath(const Kit *k, const QString &pathOnDevice)
|
|
{
|
|
if (IDevice::ConstPtr dev = device(k))
|
|
return dev->filePath(pathOnDevice);
|
|
return FilePath::fromString(pathOnDevice);
|
|
}
|
|
|
|
void DeviceKitAspectFactory::onKitsLoaded()
|
|
{
|
|
for (Kit *k : KitManager::kits())
|
|
fix(k);
|
|
|
|
DeviceManager *dm = DeviceManager::instance();
|
|
connect(dm, &DeviceManager::deviceListReplaced, this, &DeviceKitAspectFactory::devicesChanged);
|
|
connect(dm, &DeviceManager::deviceAdded, this, &DeviceKitAspectFactory::devicesChanged);
|
|
connect(dm, &DeviceManager::deviceRemoved, this, &DeviceKitAspectFactory::devicesChanged);
|
|
connect(dm, &DeviceManager::deviceUpdated, this, &DeviceKitAspectFactory::deviceUpdated);
|
|
|
|
connect(KitManager::instance(), &KitManager::kitUpdated,
|
|
this, &DeviceKitAspectFactory::kitUpdated);
|
|
connect(KitManager::instance(), &KitManager::unmanagedKitUpdated,
|
|
this, &DeviceKitAspectFactory::kitUpdated);
|
|
}
|
|
|
|
void DeviceKitAspectFactory::deviceUpdated(Id id)
|
|
{
|
|
for (Kit *k : KitManager::kits()) {
|
|
if (DeviceKitAspect::deviceId(k) == id)
|
|
notifyAboutUpdate(k);
|
|
}
|
|
}
|
|
|
|
void DeviceKitAspectFactory::kitUpdated(Kit *k)
|
|
{
|
|
setup(k); // Set default device if necessary
|
|
}
|
|
|
|
void DeviceKitAspectFactory::devicesChanged()
|
|
{
|
|
for (Kit *k : KitManager::kits())
|
|
setup(k); // Set default device if necessary
|
|
}
|
|
|
|
const DeviceKitAspectFactory theDeviceKitAspectFactory;
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
// BuildDeviceKitAspect:
|
|
// --------------------------------------------------------------------------
|
|
namespace Internal {
|
|
class BuildDeviceKitAspectImpl final : public KitAspect
|
|
{
|
|
public:
|
|
BuildDeviceKitAspectImpl(Kit *workingCopy, const KitAspectFactory *factory)
|
|
: KitAspect(workingCopy, factory),
|
|
m_comboBox(createSubWidget<QComboBox>()),
|
|
m_model(new DeviceManagerModel(DeviceManager::instance()))
|
|
{
|
|
setManagingPage(Constants::DEVICE_SETTINGS_PAGE_ID);
|
|
m_comboBox->setSizePolicy(QSizePolicy::Ignored, m_comboBox->sizePolicy().verticalPolicy());
|
|
m_comboBox->setModel(m_model);
|
|
refresh();
|
|
m_comboBox->setToolTip(factory->description());
|
|
|
|
connect(m_model, &QAbstractItemModel::modelAboutToBeReset,
|
|
this, &BuildDeviceKitAspectImpl::modelAboutToReset);
|
|
connect(m_model, &QAbstractItemModel::modelReset,
|
|
this, &BuildDeviceKitAspectImpl::modelReset);
|
|
connect(m_comboBox, &QComboBox::currentIndexChanged,
|
|
this, &BuildDeviceKitAspectImpl::currentDeviceChanged);
|
|
}
|
|
|
|
~BuildDeviceKitAspectImpl() override
|
|
{
|
|
delete m_comboBox;
|
|
delete m_model;
|
|
}
|
|
|
|
private:
|
|
void addToInnerLayout(Layouting::Layout &builder) override
|
|
{
|
|
addMutableAction(m_comboBox);
|
|
builder.addItem(m_comboBox);
|
|
}
|
|
|
|
void makeReadOnly() override { m_comboBox->setEnabled(false); }
|
|
|
|
void refresh() override
|
|
{
|
|
QList<Id> blackList;
|
|
const DeviceManager *dm = DeviceManager::instance();
|
|
for (int i = 0; i < dm->deviceCount(); ++i) {
|
|
IDevice::ConstPtr device = dm->deviceAt(i);
|
|
if (!device->usableAsBuildDevice())
|
|
blackList.append(device->id());
|
|
}
|
|
|
|
m_model->setFilter(blackList);
|
|
m_comboBox->setCurrentIndex(m_model->indexOf(BuildDeviceKitAspect::device(m_kit)));
|
|
}
|
|
|
|
void modelAboutToReset()
|
|
{
|
|
m_selectedId = m_model->deviceId(m_comboBox->currentIndex());
|
|
m_ignoreChanges.lock();
|
|
}
|
|
|
|
void modelReset()
|
|
{
|
|
m_comboBox->setCurrentIndex(m_model->indexForId(m_selectedId));
|
|
m_ignoreChanges.unlock();
|
|
}
|
|
|
|
void currentDeviceChanged()
|
|
{
|
|
if (m_ignoreChanges.isLocked())
|
|
return;
|
|
BuildDeviceKitAspect::setDeviceId(m_kit, m_model->deviceId(m_comboBox->currentIndex()));
|
|
}
|
|
|
|
Guard m_ignoreChanges;
|
|
QComboBox *m_comboBox;
|
|
DeviceManagerModel *m_model;
|
|
Id m_selectedId;
|
|
};
|
|
} // namespace Internal
|
|
|
|
class BuildDeviceKitAspectFactory : public KitAspectFactory
|
|
{
|
|
public:
|
|
BuildDeviceKitAspectFactory();
|
|
|
|
private:
|
|
void setup(Kit *k) override;
|
|
Tasks validate(const Kit *k) const override;
|
|
|
|
KitAspect *createKitAspect(Kit *k) const override;
|
|
|
|
QString displayNamePostfix(const Kit *k) const override;
|
|
|
|
ItemList toUserOutput(const Kit *k) const override;
|
|
|
|
void addToMacroExpander(Kit *kit, MacroExpander *expander) const override;
|
|
|
|
void onKitsLoaded() override;
|
|
void deviceUpdated(Id dataId);
|
|
void devicesChanged();
|
|
void kitUpdated(Kit *k);
|
|
};
|
|
|
|
BuildDeviceKitAspectFactory::BuildDeviceKitAspectFactory()
|
|
{
|
|
setId(BuildDeviceKitAspect::id());
|
|
setDisplayName(Tr::tr("Build device"));
|
|
setDescription(Tr::tr("The device used to build applications on."));
|
|
setPriority(31900);
|
|
}
|
|
|
|
static IDeviceConstPtr defaultDevice()
|
|
{
|
|
return DeviceManager::defaultDesktopDevice();
|
|
}
|
|
|
|
void BuildDeviceKitAspectFactory::setup(Kit *k)
|
|
{
|
|
QTC_ASSERT(DeviceManager::instance()->isLoaded(), return );
|
|
IDevice::ConstPtr dev = BuildDeviceKitAspect::device(k);
|
|
if (dev)
|
|
return;
|
|
|
|
dev = defaultDevice();
|
|
BuildDeviceKitAspect::setDeviceId(k, dev ? dev->id() : Id());
|
|
}
|
|
|
|
Tasks BuildDeviceKitAspectFactory::validate(const Kit *k) const
|
|
{
|
|
IDevice::ConstPtr dev = BuildDeviceKitAspect::device(k);
|
|
Tasks result;
|
|
if (!dev)
|
|
result.append(BuildSystemTask(Task::Warning, Tr::tr("No build device set.")));
|
|
|
|
return result;
|
|
}
|
|
|
|
KitAspect *BuildDeviceKitAspectFactory::createKitAspect(Kit *k) const
|
|
{
|
|
QTC_ASSERT(k, return nullptr);
|
|
return new Internal::BuildDeviceKitAspectImpl(k, this);
|
|
}
|
|
|
|
QString BuildDeviceKitAspectFactory::displayNamePostfix(const Kit *k) const
|
|
{
|
|
IDevice::ConstPtr dev = BuildDeviceKitAspect::device(k);
|
|
return dev ? dev->displayName() : QString();
|
|
}
|
|
|
|
KitAspectFactory::ItemList BuildDeviceKitAspectFactory::toUserOutput(const Kit *k) const
|
|
{
|
|
IDevice::ConstPtr dev = BuildDeviceKitAspect::device(k);
|
|
return {{Tr::tr("Build device"), dev ? dev->displayName() : Tr::tr("Unconfigured")}};
|
|
}
|
|
|
|
void BuildDeviceKitAspectFactory::addToMacroExpander(Kit *kit, MacroExpander *expander) const
|
|
{
|
|
QTC_ASSERT(kit, return);
|
|
expander->registerVariable("BuildDevice:HostAddress", Tr::tr("Build host address"), [kit] {
|
|
const IDevice::ConstPtr device = BuildDeviceKitAspect::device(kit);
|
|
return device ? device->sshParameters().host() : QString();
|
|
});
|
|
expander->registerVariable("BuildDevice:SshPort", Tr::tr("Build SSH port"), [kit] {
|
|
const IDevice::ConstPtr device = BuildDeviceKitAspect::device(kit);
|
|
return device ? QString::number(device->sshParameters().port()) : QString();
|
|
});
|
|
expander->registerVariable("BuildDevice:UserName", Tr::tr("Build user name"), [kit] {
|
|
const IDevice::ConstPtr device = BuildDeviceKitAspect::device(kit);
|
|
return device ? device->sshParameters().userName() : QString();
|
|
});
|
|
expander->registerVariable("BuildDevice:KeyFile", Tr::tr("Build private key file"), [kit] {
|
|
const IDevice::ConstPtr device = BuildDeviceKitAspect::device(kit);
|
|
return device ? device->sshParameters().privateKeyFile.toString() : QString();
|
|
});
|
|
expander->registerVariable("BuildDevice:Name", Tr::tr("Build device name"), [kit] {
|
|
const IDevice::ConstPtr device = BuildDeviceKitAspect::device(kit);
|
|
return device ? device->displayName() : QString();
|
|
});
|
|
expander
|
|
->registerFileVariables("BuildDevice::Root", Tr::tr("Build device root directory"), [kit] {
|
|
const IDevice::ConstPtr device = BuildDeviceKitAspect::device(kit);
|
|
return device ? device->rootPath() : FilePath{};
|
|
});
|
|
}
|
|
|
|
Id BuildDeviceKitAspect::id()
|
|
{
|
|
return "PE.Profile.BuildDevice";
|
|
}
|
|
|
|
IDevice::ConstPtr BuildDeviceKitAspect::device(const Kit *k)
|
|
{
|
|
QTC_ASSERT(DeviceManager::instance()->isLoaded(), return IDevice::ConstPtr());
|
|
IDevice::ConstPtr dev = DeviceManager::instance()->find(deviceId(k));
|
|
if (!dev)
|
|
dev = defaultDevice();
|
|
return dev;
|
|
}
|
|
|
|
Id BuildDeviceKitAspect::deviceId(const Kit *k)
|
|
{
|
|
return k ? Id::fromSetting(k->value(BuildDeviceKitAspect::id())) : Id();
|
|
}
|
|
|
|
void BuildDeviceKitAspect::setDevice(Kit *k, IDevice::ConstPtr dev)
|
|
{
|
|
setDeviceId(k, dev ? dev->id() : Id());
|
|
}
|
|
|
|
void BuildDeviceKitAspect::setDeviceId(Kit *k, Id id)
|
|
{
|
|
QTC_ASSERT(k, return);
|
|
k->setValue(BuildDeviceKitAspect::id(), id.toSetting());
|
|
}
|
|
|
|
void BuildDeviceKitAspectFactory::onKitsLoaded()
|
|
{
|
|
for (Kit *k : KitManager::kits())
|
|
fix(k);
|
|
|
|
DeviceManager *dm = DeviceManager::instance();
|
|
connect(dm, &DeviceManager::deviceListReplaced,
|
|
this, &BuildDeviceKitAspectFactory::devicesChanged);
|
|
connect(dm, &DeviceManager::deviceAdded,
|
|
this, &BuildDeviceKitAspectFactory::devicesChanged);
|
|
connect(dm, &DeviceManager::deviceRemoved,
|
|
this, &BuildDeviceKitAspectFactory::devicesChanged);
|
|
connect(dm, &DeviceManager::deviceUpdated,
|
|
this, &BuildDeviceKitAspectFactory::deviceUpdated);
|
|
connect(KitManager::instance(), &KitManager::kitUpdated,
|
|
this, &BuildDeviceKitAspectFactory::kitUpdated);
|
|
connect(KitManager::instance(), &KitManager::unmanagedKitUpdated,
|
|
this, &BuildDeviceKitAspectFactory::kitUpdated);
|
|
}
|
|
|
|
void BuildDeviceKitAspectFactory::deviceUpdated(Id id)
|
|
{
|
|
const QList<Kit *> kits = KitManager::kits();
|
|
for (Kit *k : kits) {
|
|
if (BuildDeviceKitAspect::deviceId(k) == id)
|
|
notifyAboutUpdate(k);
|
|
}
|
|
}
|
|
|
|
void BuildDeviceKitAspectFactory::kitUpdated(Kit *k)
|
|
{
|
|
setup(k); // Set default device if necessary
|
|
}
|
|
|
|
void BuildDeviceKitAspectFactory::devicesChanged()
|
|
{
|
|
const QList<Kit *> kits = KitManager::kits();
|
|
for (Kit *k : kits)
|
|
setup(k); // Set default device if necessary
|
|
}
|
|
|
|
const BuildDeviceKitAspectFactory theBuildDeviceKitAspectFactory;
|
|
|
|
// --------------------------------------------------------------------------
|
|
// EnvironmentKitAspect:
|
|
// --------------------------------------------------------------------------
|
|
static EnvironmentItem forceMSVCEnglishItem()
|
|
{
|
|
static EnvironmentItem item("VSLANG", "1033");
|
|
return item;
|
|
}
|
|
|
|
static bool enforcesMSVCEnglish(const EnvironmentItems &changes)
|
|
{
|
|
return changes.contains(forceMSVCEnglishItem());
|
|
}
|
|
|
|
namespace Internal {
|
|
class EnvironmentKitAspectImpl final : public KitAspect
|
|
{
|
|
public:
|
|
EnvironmentKitAspectImpl(Kit *workingCopy, const KitAspectFactory *factory)
|
|
: KitAspect(workingCopy, factory),
|
|
m_summaryLabel(createSubWidget<ElidingLabel>()),
|
|
m_manageButton(createSubWidget<QPushButton>()),
|
|
m_mainWidget(createSubWidget<QWidget>())
|
|
{
|
|
auto *layout = new QVBoxLayout;
|
|
layout->setContentsMargins(0, 0, 0, 0);
|
|
layout->addWidget(m_summaryLabel);
|
|
if (HostOsInfo::isWindowsHost())
|
|
initMSVCOutputSwitch(layout);
|
|
m_mainWidget->setLayout(layout);
|
|
refresh();
|
|
m_manageButton->setText(Tr::tr("Change..."));
|
|
connect(m_manageButton, &QAbstractButton::clicked,
|
|
this, &EnvironmentKitAspectImpl::editEnvironmentChanges);
|
|
}
|
|
|
|
private:
|
|
void addToInnerLayout(Layouting::Layout &builder) override
|
|
{
|
|
addMutableAction(m_mainWidget);
|
|
builder.addItem(m_mainWidget);
|
|
builder.addItem(m_manageButton);
|
|
}
|
|
|
|
void makeReadOnly() override { m_manageButton->setEnabled(false); }
|
|
|
|
void refresh() override
|
|
{
|
|
const EnvironmentItems changes = envWithoutMSVCEnglishEnforcement();
|
|
const QString shortSummary = EnvironmentItem::toStringList(changes).join("; ");
|
|
m_summaryLabel->setText(shortSummary.isEmpty() ? Tr::tr("No changes to apply.") : shortSummary);
|
|
}
|
|
|
|
void editEnvironmentChanges()
|
|
{
|
|
MacroExpander *expander = m_kit->macroExpander();
|
|
EnvironmentDialog::Polisher polisher = [expander](QWidget *w) {
|
|
VariableChooser::addSupportForChildWidgets(w, expander);
|
|
};
|
|
auto changes = EnvironmentDialog::getEnvironmentItems(m_summaryLabel,
|
|
envWithoutMSVCEnglishEnforcement(),
|
|
QString(),
|
|
polisher);
|
|
if (!changes)
|
|
return;
|
|
|
|
if (HostOsInfo::isWindowsHost()) {
|
|
// re-add what envWithoutMSVCEnglishEnforcement removed
|
|
// or update vslang checkbox if user added it manually
|
|
if (m_vslangCheckbox->isChecked() && !enforcesMSVCEnglish(*changes))
|
|
changes->append(forceMSVCEnglishItem());
|
|
else if (enforcesMSVCEnglish(*changes))
|
|
m_vslangCheckbox->setChecked(true);
|
|
}
|
|
EnvironmentKitAspect::setEnvironmentChanges(m_kit, *changes);
|
|
}
|
|
|
|
EnvironmentItems envWithoutMSVCEnglishEnforcement() const
|
|
{
|
|
EnvironmentItems changes = EnvironmentKitAspect::environmentChanges(m_kit);
|
|
|
|
if (HostOsInfo::isWindowsHost())
|
|
changes.removeAll(forceMSVCEnglishItem());
|
|
|
|
return changes;
|
|
}
|
|
|
|
void initMSVCOutputSwitch(QVBoxLayout *layout)
|
|
{
|
|
m_vslangCheckbox = new QCheckBox(Tr::tr("Force UTF-8 MSVC compiler output"));
|
|
layout->addWidget(m_vslangCheckbox);
|
|
m_vslangCheckbox->setToolTip(Tr::tr("Either switches MSVC to English or keeps the language and "
|
|
"just forces UTF-8 output (may vary depending on the used MSVC "
|
|
"compiler)."));
|
|
if (enforcesMSVCEnglish(EnvironmentKitAspect::environmentChanges(m_kit)))
|
|
m_vslangCheckbox->setChecked(true);
|
|
connect(m_vslangCheckbox, &QCheckBox::clicked, this, [this](bool checked) {
|
|
EnvironmentItems changes = EnvironmentKitAspect::environmentChanges(m_kit);
|
|
if (!checked && changes.indexOf(forceMSVCEnglishItem()) >= 0)
|
|
changes.removeAll(forceMSVCEnglishItem());
|
|
if (checked && changes.indexOf(forceMSVCEnglishItem()) < 0)
|
|
changes.append(forceMSVCEnglishItem());
|
|
EnvironmentKitAspect::setEnvironmentChanges(m_kit, changes);
|
|
});
|
|
}
|
|
|
|
ElidingLabel *m_summaryLabel;
|
|
QPushButton *m_manageButton;
|
|
QCheckBox *m_vslangCheckbox;
|
|
QWidget *m_mainWidget;
|
|
};
|
|
} // namespace Internal
|
|
|
|
class EnvironmentKitAspectFactory : public KitAspectFactory
|
|
{
|
|
public:
|
|
EnvironmentKitAspectFactory();
|
|
|
|
Tasks validate(const Kit *k) const override;
|
|
void fix(Kit *k) override;
|
|
|
|
void addToBuildEnvironment(const Kit *k, Environment &env) const override;
|
|
void addToRunEnvironment(const Kit *, Environment &) const override;
|
|
|
|
KitAspect *createKitAspect(Kit *k) const override;
|
|
|
|
ItemList toUserOutput(const Kit *k) const override;
|
|
};
|
|
|
|
EnvironmentKitAspectFactory::EnvironmentKitAspectFactory()
|
|
{
|
|
setId(EnvironmentKitAspect::id());
|
|
setDisplayName(Tr::tr("Environment"));
|
|
setDescription(Tr::tr("Additional build environment settings when using this kit."));
|
|
setPriority(29000);
|
|
}
|
|
|
|
Tasks EnvironmentKitAspectFactory::validate(const Kit *k) const
|
|
{
|
|
Tasks result;
|
|
QTC_ASSERT(k, return result);
|
|
|
|
const QVariant variant = k->value(EnvironmentKitAspect::id());
|
|
if (!variant.isNull() && !variant.canConvert(QMetaType(QMetaType::QVariantList)))
|
|
result << BuildSystemTask(Task::Error, Tr::tr("The environment setting value is invalid."));
|
|
|
|
return result;
|
|
}
|
|
|
|
void EnvironmentKitAspectFactory::fix(Kit *k)
|
|
{
|
|
QTC_ASSERT(k, return);
|
|
|
|
const QVariant variant = k->value(EnvironmentKitAspect::id());
|
|
if (!variant.isNull() && !variant.canConvert(QMetaType(QMetaType::QVariantList))) {
|
|
qWarning("Kit \"%s\" has a wrong environment value set.", qPrintable(k->displayName()));
|
|
EnvironmentKitAspect::setEnvironmentChanges(k, EnvironmentItems());
|
|
}
|
|
}
|
|
|
|
void EnvironmentKitAspectFactory::addToBuildEnvironment(const Kit *k, Environment &env) const
|
|
{
|
|
const QStringList values
|
|
= transform(EnvironmentItem::toStringList(EnvironmentKitAspect::environmentChanges(k)),
|
|
[k](const QString &v) { return k->macroExpander()->expand(v); });
|
|
env.modify(EnvironmentItem::fromStringList(values));
|
|
}
|
|
|
|
void EnvironmentKitAspectFactory::addToRunEnvironment(const Kit *k, Environment &env) const
|
|
{
|
|
addToBuildEnvironment(k, env);
|
|
}
|
|
|
|
KitAspect *EnvironmentKitAspectFactory::createKitAspect(Kit *k) const
|
|
{
|
|
QTC_ASSERT(k, return nullptr);
|
|
return new Internal::EnvironmentKitAspectImpl(k, this);
|
|
}
|
|
|
|
KitAspectFactory::ItemList EnvironmentKitAspectFactory::toUserOutput(const Kit *k) const
|
|
{
|
|
return {{Tr::tr("Environment"),
|
|
EnvironmentItem::toStringList(EnvironmentKitAspect::environmentChanges(k)).join("<br>")}};
|
|
}
|
|
|
|
Id EnvironmentKitAspect::id()
|
|
{
|
|
return "PE.Profile.Environment";
|
|
}
|
|
|
|
EnvironmentItems EnvironmentKitAspect::environmentChanges(const Kit *k)
|
|
{
|
|
if (k)
|
|
return EnvironmentItem::fromStringList(k->value(EnvironmentKitAspect::id()).toStringList());
|
|
return {};
|
|
}
|
|
|
|
void EnvironmentKitAspect::setEnvironmentChanges(Kit *k, const EnvironmentItems &changes)
|
|
{
|
|
if (k)
|
|
k->setValue(EnvironmentKitAspect::id(), EnvironmentItem::toStringList(changes));
|
|
}
|
|
|
|
const EnvironmentKitAspectFactory theEnvironmentKitAspectFactory;
|
|
|
|
} // namespace ProjectExplorer
|