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:
Christian Kandeler
2019-03-14 18:07:56 +01:00
parent a2c7257c9b
commit e94f5b496e
7 changed files with 125 additions and 19 deletions

View File

@@ -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;
}

View File

@@ -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(),

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;

View File

@@ -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

View File

@@ -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);