From e94f5b496ecd9ca212009bd24368265e1df8199b Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Thu, 14 Mar 2019 18:07:56 +0100 Subject: [PATCH] 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 --- src/plugins/projectexplorer/abi.cpp | 4 +- src/plugins/projectexplorer/kit.cpp | 11 ++++ src/plugins/projectexplorer/kit.h | 6 ++ src/plugins/projectexplorer/kitmanager.cpp | 70 +++++++++++++++++++--- src/plugins/projectexplorer/kitmanager.h | 2 + src/plugins/qtsupport/qtkitinformation.cpp | 49 +++++++++++---- src/plugins/qtsupport/qtkitinformation.h | 2 + 7 files changed, 125 insertions(+), 19 deletions(-) diff --git a/src/plugins/projectexplorer/abi.cpp b/src/plugins/projectexplorer/abi.cpp index 1227ae52157..dade2e436e2 100644 --- a/src/plugins/projectexplorer/abi.cpp +++ b/src/plugins/projectexplorer/abi.cpp @@ -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; } diff --git a/src/plugins/projectexplorer/kit.cpp b/src/plugins/projectexplorer/kit.cpp index ddc4d00c7ca..6b9f1d75fab 100644 --- a/src/plugins/projectexplorer/kit.cpp +++ b/src/plugins/projectexplorer/kit.cpp @@ -46,6 +46,8 @@ #include #include +#include + using namespace Core; using namespace Utils; @@ -354,6 +356,15 @@ Id Kit::id() const return d->m_id; } +int Kit::weight() const +{ + const QList &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(), diff --git a/src/plugins/projectexplorer/kit.h b/src/plugins/projectexplorer/kit.h index adfef62c0f7..81d734c71b9 100644 --- a/src/plugins/projectexplorer/kit.h +++ b/src/plugins/projectexplorer/kit.h @@ -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); diff --git a/src/plugins/projectexplorer/kitmanager.cpp b/src/plugins/projectexplorer/kitmanager.cpp index 4f31e674ab5..4a9d513f55d 100644 --- a/src/plugins/projectexplorer/kitmanager.cpp +++ b/src/plugins/projectexplorer/kitmanager.cpp @@ -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 @@ -40,6 +44,7 @@ #include #include +#include #include #include @@ -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(); // 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 allToolchains = ToolChainManager::toolChains(isHostToolchain); + QHash> 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->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); diff --git a/src/plugins/projectexplorer/kitmanager.h b/src/plugins/projectexplorer/kitmanager.h index 0a1fc213a10..8fb2f261644 100644 --- a/src/plugins/projectexplorer/kitmanager.h +++ b/src/plugins/projectexplorer/kitmanager.h @@ -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; diff --git a/src/plugins/qtsupport/qtkitinformation.cpp b/src/plugins/qtsupport/qtkitinformation.cpp index 1181e5b359d..3cd0b2d36b7 100644 --- a/src/plugins/qtsupport/qtkitinformation.cpp +++ b/src/plugins/qtsupport/qtkitinformation.cpp @@ -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 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 exactMatches = Utils::filtered(matches, + [&tcAbi](const BaseQtVersion *qt) { + return qt->qtAbis().contains(tcAbi); + }); + const QList &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 QtKitAspect::validate(const ProjectExplorer::Kit *k) const @@ -370,4 +385,18 @@ QSet QtKitAspect::availableFeatures(const Kit *k) const return version ? version->features() : QSet(); } +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 diff --git a/src/plugins/qtsupport/qtkitinformation.h b/src/plugins/qtsupport/qtkitinformation.h index 5a9be938029..565e5aef0f9 100644 --- a/src/plugins/qtsupport/qtkitinformation.h +++ b/src/plugins/qtsupport/qtkitinformation.h @@ -73,6 +73,8 @@ public: QSet availableFeatures(const ProjectExplorer::Kit *k) const override; private: + int weight(const ProjectExplorer::Kit *k) const override; + void qtVersionsChanged(const QList &addedIds, const QList &removedIds, const QList &changedIds);