forked from qt-creator/qt-creator
Improve kit auto-detection
The old code was too simplistic: Basically, we just took a random toolchain, slapped a random Qt onto it and made that the default kit. Instead, we now go through all toolchains, try to find a matching Qt version, debugger etc and create a kit for that combination unless there is a better one. Fixes: QTCREATORBUG-22138 Change-Id: Ib57ca4453a93ee9253c75398328c3bca33087dc6 Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
@@ -626,8 +626,10 @@ bool Abi::isCompatibleWith(const Abi &other) const
|
||||
if (isCompat && (osFlavor() == AndroidLinuxFlavor || other.osFlavor() == AndroidLinuxFlavor))
|
||||
isCompat = (architecture() == other.architecture()) && (osFlavor() == other.osFlavor());
|
||||
|
||||
if (!isCompat && compatibleMSVCFlavors(osFlavor(), other.osFlavor()))
|
||||
if (!isCompat && wordWidth() == other.wordWidth()
|
||||
&& compatibleMSVCFlavors(osFlavor(), other.osFlavor())) {
|
||||
isCompat = true;
|
||||
}
|
||||
|
||||
return isCompat;
|
||||
}
|
||||
|
@@ -46,6 +46,8 @@
|
||||
#include <QTextStream>
|
||||
#include <QUuid>
|
||||
|
||||
#include <numeric>
|
||||
|
||||
using namespace Core;
|
||||
using namespace Utils;
|
||||
|
||||
@@ -354,6 +356,15 @@ Id Kit::id() const
|
||||
return d->m_id;
|
||||
}
|
||||
|
||||
int Kit::weight() const
|
||||
{
|
||||
const QList<KitAspect *> &aspects = KitManager::kitAspects();
|
||||
return std::accumulate(aspects.begin(), aspects.end(), 0,
|
||||
[this](int sum, const KitAspect *aspect) {
|
||||
return sum + aspect->weight(this);
|
||||
});
|
||||
}
|
||||
|
||||
static QIcon iconForDeviceType(Core::Id deviceType)
|
||||
{
|
||||
const IDeviceFactory *factory = Utils::findOrDefault(IDeviceFactory::allDeviceFactories(),
|
||||
|
@@ -90,6 +90,12 @@ public:
|
||||
bool isSdkProvided() const;
|
||||
Core::Id id() const;
|
||||
|
||||
// The higher the weight, the more aspects have sensible values for this kit.
|
||||
// For instance, a kit where a matching debugger was found for the toolchain will have a
|
||||
// higher weight than one whose toolchain does not match a known debugger, assuming
|
||||
// all other aspects are equal.
|
||||
int weight() const;
|
||||
|
||||
QIcon icon() const;
|
||||
Utils::FileName iconPath() const;
|
||||
void setIconPath(const Utils::FileName &path);
|
||||
|
@@ -25,12 +25,16 @@
|
||||
|
||||
#include "kitmanager.h"
|
||||
|
||||
#include "abi.h"
|
||||
#include "devicesupport/idevicefactory.h"
|
||||
#include "kit.h"
|
||||
#include "kitfeatureprovider.h"
|
||||
#include "kitinformation.h"
|
||||
#include "kitmanagerconfigwidget.h"
|
||||
#include "project.h"
|
||||
#include "projectexplorerconstants.h"
|
||||
#include "task.h"
|
||||
#include "toolchainmanager.h"
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
|
||||
@@ -40,6 +44,7 @@
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/stringutils.h>
|
||||
|
||||
#include <QHash>
|
||||
#include <QSettings>
|
||||
#include <QStyle>
|
||||
|
||||
@@ -220,16 +225,60 @@ void KitManager::restoreKits()
|
||||
// Delete all loaded autodetected kits that were not rediscovered:
|
||||
kitsToCheck.clear();
|
||||
|
||||
if (resultList.size() == 0) {
|
||||
auto defaultKit = std::make_unique<Kit>(); // One kit using default values
|
||||
defaultKit->setUnexpandedDisplayName(tr("Desktop"));
|
||||
defaultKit->setSdkProvided(false);
|
||||
defaultKit->setAutoDetected(false);
|
||||
if (resultList.empty()) {
|
||||
// No kits exist yet, so let's try to autoconfigure some from the toolchains we know.
|
||||
// We consider only host toolchains, because for other ones we lack the knowledge how to
|
||||
// map them to their respective device type.
|
||||
static const auto isHostToolchain = [](const ToolChain *tc) {
|
||||
static const Abi hostAbi = Abi::hostAbi();
|
||||
const Abi tcAbi = tc->targetAbi();
|
||||
return tcAbi.os() == hostAbi.os() && tcAbi.architecture() == hostAbi.architecture()
|
||||
&& (tcAbi.os() != Abi::LinuxOS || tcAbi.osFlavor() == hostAbi.osFlavor());
|
||||
};
|
||||
const QList<ToolChain *> allToolchains = ToolChainManager::toolChains(isHostToolchain);
|
||||
QHash<Abi, QHash<Core::Id, ToolChain *>> uniqueToolchains;
|
||||
|
||||
defaultKit->setup();
|
||||
// On Linux systems, we usually detect a plethora of same-ish toolchains. The following
|
||||
// algorithm gives precedence to icecc and ccache and otherwise simply chooses the one with
|
||||
// the shortest path. This should also take care of ensuring matching C/C++ pairs.
|
||||
// TODO: This should not need to be done here. Instead, it should be a convenience
|
||||
// operation on some lower level, e.g. in the toolchain class(es).
|
||||
// Also, we shouldn't detect so many doublets in the first place.
|
||||
for (ToolChain * const tc : allToolchains) {
|
||||
ToolChain *&bestTc = uniqueToolchains[tc->targetAbi()][tc->language()];
|
||||
if (!bestTc) {
|
||||
bestTc = tc;
|
||||
continue;
|
||||
}
|
||||
const QString bestFilePath = bestTc->compilerCommand().toString();
|
||||
const QString currentFilePath = tc->compilerCommand().toString();
|
||||
if ((currentFilePath.contains("icecc") && !bestFilePath.contains("icecc"))
|
||||
|| (currentFilePath.contains("ccache") && !bestFilePath.contains("ccache")
|
||||
&& !bestFilePath.contains("icecc"))
|
||||
|| (bestFilePath.length() > currentFilePath.length())) {
|
||||
bestTc = tc;
|
||||
}
|
||||
}
|
||||
|
||||
completeKit(defaultKit.get()); // Store manual kits
|
||||
resultList.emplace_back(std::move(defaultKit));
|
||||
int maxWeight = 0;
|
||||
for (auto it = uniqueToolchains.cbegin(); it != uniqueToolchains.cend(); ++it) {
|
||||
auto kit = std::make_unique<Kit>();
|
||||
kit->setSdkProvided(false);
|
||||
kit->setAutoDetected(false); // TODO: Why false? What does autodetected mean here?
|
||||
for (ToolChain * const tc : it.value())
|
||||
ToolChainKitAspect::setToolChain(kit.get(), tc);
|
||||
kit->setUnexpandedDisplayName(tr("Desktop (%1)").arg(it.key().toString()));
|
||||
kit->setup();
|
||||
if (kit->weight() < maxWeight)
|
||||
continue;
|
||||
if (kit->weight() > maxWeight) {
|
||||
maxWeight = kit->weight();
|
||||
resultList.clear();
|
||||
}
|
||||
resultList.emplace_back(std::move(kit));
|
||||
}
|
||||
if (resultList.size() == 1)
|
||||
resultList.front()->setUnexpandedDisplayName(tr("Desktop"));
|
||||
}
|
||||
|
||||
Kit *k = Utils::findOrDefault(resultList, Utils::equal(&Kit::id, defaultUserKit));
|
||||
@@ -515,6 +564,11 @@ KitAspect::~KitAspect()
|
||||
KitManager::deregisterKitAspect(this);
|
||||
}
|
||||
|
||||
int KitAspect::weight(const Kit *k) const
|
||||
{
|
||||
return k->value(id()).isValid() ? 1 : 0;
|
||||
}
|
||||
|
||||
void KitAspect::addToEnvironment(const Kit *k, Environment &env) const
|
||||
{
|
||||
Q_UNUSED(k);
|
||||
|
@@ -88,6 +88,8 @@ public:
|
||||
// called on initial setup of a kit.
|
||||
virtual void setup(Kit *) { return; }
|
||||
|
||||
virtual int weight(const Kit *k) const;
|
||||
|
||||
virtual ItemList toUserOutput(const Kit *) const = 0;
|
||||
|
||||
virtual KitAspectWidget *createConfigWidget(Kit *) const = 0;
|
||||
|
@@ -163,17 +163,32 @@ QtKitAspect::QtKitAspect()
|
||||
|
||||
void QtKitAspect::setup(ProjectExplorer::Kit *k)
|
||||
{
|
||||
Q_UNUSED(k);
|
||||
const Abi tcAbi = ToolChainKitAspect::targetAbi(k);
|
||||
const Core::Id deviceType = DeviceTypeKitAspect::deviceTypeId(k);
|
||||
|
||||
// find "Qt in PATH":
|
||||
BaseQtVersion *result = QtVersionManager::version(equal(&BaseQtVersion::autodetectionSource,
|
||||
QString::fromLatin1("PATH")));
|
||||
if (!result) {
|
||||
// Use *any* desktop Qt:
|
||||
result = QtVersionManager::version(equal(&BaseQtVersion::type,
|
||||
QString::fromLatin1(QtSupport::Constants::DESKTOPQT)));
|
||||
}
|
||||
k->setValue(id(), result ? result->uniqueId() : -1);
|
||||
const QList<BaseQtVersion *> matches
|
||||
= QtVersionManager::versions([&tcAbi, &deviceType](const BaseQtVersion *qt) {
|
||||
return qt->targetDeviceTypes().contains(deviceType)
|
||||
&& Utils::contains(qt->qtAbis(), [&tcAbi](const Abi &qtAbi) {
|
||||
return qtAbi.isCompatibleWith(tcAbi); });
|
||||
});
|
||||
if (matches.empty())
|
||||
return;
|
||||
|
||||
// An MSVC 2015 toolchain is compatible with an MSVC 2017 Qt, but we prefer an
|
||||
// MSVC 2015 Qt if we find one.
|
||||
const QList<BaseQtVersion *> exactMatches = Utils::filtered(matches,
|
||||
[&tcAbi](const BaseQtVersion *qt) {
|
||||
return qt->qtAbis().contains(tcAbi);
|
||||
});
|
||||
const QList<BaseQtVersion *> &candidates = !exactMatches.empty() ? exactMatches : matches;
|
||||
|
||||
BaseQtVersion * const qtFromPath = QtVersionManager::version(
|
||||
equal(&BaseQtVersion::autodetectionSource, QString::fromLatin1("PATH")));
|
||||
if (qtFromPath && candidates.contains(qtFromPath))
|
||||
k->setValue(id(), qtFromPath->uniqueId());
|
||||
else
|
||||
k->setValue(id(), candidates.first()->uniqueId());
|
||||
}
|
||||
|
||||
QList<ProjectExplorer::Task> QtKitAspect::validate(const ProjectExplorer::Kit *k) const
|
||||
@@ -370,4 +385,18 @@ QSet<Core::Id> QtKitAspect::availableFeatures(const Kit *k) const
|
||||
return version ? version->features() : QSet<Core::Id>();
|
||||
}
|
||||
|
||||
int QtKitAspect::weight(const Kit *k) const
|
||||
{
|
||||
const BaseQtVersion * const qt = qtVersion(k);
|
||||
if (!qt)
|
||||
return 0;
|
||||
if (!qt->targetDeviceTypes().contains(DeviceTypeKitAspect::deviceTypeId(k)))
|
||||
return 0;
|
||||
const Abi tcAbi = ToolChainKitAspect::targetAbi(k);
|
||||
if (qt->qtAbis().contains(tcAbi))
|
||||
return 2;
|
||||
return Utils::contains(qt->qtAbis(), [&tcAbi](const Abi &qtAbi) {
|
||||
return qtAbi.isCompatibleWith(tcAbi); }) ? 1 : 0;
|
||||
}
|
||||
|
||||
} // namespace QtSupport
|
||||
|
@@ -73,6 +73,8 @@ public:
|
||||
QSet<Core::Id> availableFeatures(const ProjectExplorer::Kit *k) const override;
|
||||
|
||||
private:
|
||||
int weight(const ProjectExplorer::Kit *k) const override;
|
||||
|
||||
void qtVersionsChanged(const QList<int> &addedIds,
|
||||
const QList<int> &removedIds,
|
||||
const QList<int> &changedIds);
|
||||
|
Reference in New Issue
Block a user