From 28b70672b40cadc61d34ff58dbd1e2c5500f28be Mon Sep 17 00:00:00 2001 From: David Schulz Date: Thu, 19 Oct 2023 13:00:48 +0200 Subject: [PATCH] Python: generate Python kits for new interpreter Change-Id: Ie4b23aae296f66900fba85a9e243bbf656e49ed4 Reviewed-by: Christian Kandeler --- src/plugins/python/pythonplugin.cpp | 5 ++ src/plugins/python/pythonsettings.cpp | 111 +++++++++++++++++++++++++- src/plugins/python/pythonsettings.h | 3 + 3 files changed, 118 insertions(+), 1 deletion(-) diff --git a/src/plugins/python/pythonplugin.cpp b/src/plugins/python/pythonplugin.cpp index a7e8cc3c747..fdfc0095554 100644 --- a/src/plugins/python/pythonplugin.cpp +++ b/src/plugins/python/pythonplugin.cpp @@ -5,6 +5,7 @@ #include "pysidebuildconfiguration.h" #include "pythoneditor.h" +#include "pythonkitaspect.h" #include "pythonproject.h" #include "pythonrunconfiguration.h" #include "pythonsettings.h" @@ -13,6 +14,7 @@ #include #include +#include #include #include #include @@ -60,6 +62,9 @@ void PythonPlugin::initialize() { d = new PythonPluginPrivate; + KitManager::setIrrelevantAspects(KitManager::irrelevantAspects() + + QSet{PythonKitAspect::id()}); + ProjectManager::registerProjectType(PythonMimeType); ProjectManager::registerProjectType(PythonMimeTypeLegacy); } diff --git a/src/plugins/python/pythonsettings.cpp b/src/plugins/python/pythonsettings.cpp index 410714af090..4e68f542519 100644 --- a/src/plugins/python/pythonsettings.cpp +++ b/src/plugins/python/pythonsettings.cpp @@ -4,6 +4,7 @@ #include "pythonsettings.h" #include "pythonconstants.h" +#include "pythonkitaspect.h" #include "pythonplugin.h" #include "pythontr.h" #include "pythonutils.h" @@ -11,6 +12,9 @@ #include #include +#include +#include + #include #include @@ -144,6 +148,7 @@ private: InterpreterDetailsWidget *m_detailsWidget = nullptr; QPushButton *m_deleteButton = nullptr; QPushButton *m_makeDefaultButton = nullptr; + QPushButton *m_generateKitButton = nullptr; QPushButton *m_cleanButton = nullptr; QString m_defaultId; @@ -153,6 +158,7 @@ private: void addItem(); void deleteItem(); void makeDefault(); + void generateKit(); void cleanUp(); }; @@ -199,6 +205,8 @@ InterpreterOptionsWidget::InterpreterOptionsWidget() m_deleteButton->setEnabled(false); m_makeDefaultButton = new QPushButton(Tr::tr("&Make Default")); m_makeDefaultButton->setEnabled(false); + m_generateKitButton = new QPushButton(Tr::tr("&Generate Kit")); + m_generateKitButton->setEnabled(false); m_cleanButton = new QPushButton(Tr::tr("&Clean Up"), this); m_cleanButton->setToolTip(Tr::tr("Remove all Python interpreters without a valid executable.")); @@ -209,6 +217,7 @@ InterpreterOptionsWidget::InterpreterOptionsWidget() addButton, m_deleteButton, m_makeDefaultButton, + m_generateKitButton, m_cleanButton, st }; @@ -230,6 +239,7 @@ InterpreterOptionsWidget::InterpreterOptionsWidget() connect(addButton, &QPushButton::pressed, this, &InterpreterOptionsWidget::addItem); connect(m_deleteButton, &QPushButton::pressed, this, &InterpreterOptionsWidget::deleteItem); connect(m_makeDefaultButton, &QPushButton::pressed, this, &InterpreterOptionsWidget::makeDefault); + connect(m_generateKitButton, &QPushButton::pressed, this, &InterpreterOptionsWidget::generateKit); connect(m_cleanButton, &QPushButton::pressed, this, &InterpreterOptionsWidget::cleanUp); connect(m_detailsWidget, &InterpreterDetailsWidget::changed, @@ -275,8 +285,11 @@ void InterpreterOptionsWidget::currentChanged(const QModelIndex &index, const QM if (index.isValid()) { m_detailsWidget->updateInterpreter(m_model.itemAt(index.row())->itemData); m_detailsWidget->show(); + m_generateKitButton->setEnabled( + !KitManager::kit(Id::fromString(m_model.itemAt(index.row())->itemData.id))); } else { m_detailsWidget->hide(); + m_generateKitButton->setEnabled(false); } m_deleteButton->setEnabled(index.isValid()); m_makeDefaultButton->setEnabled(index.isValid()); @@ -553,6 +566,13 @@ void InterpreterOptionsWidget::makeDefault() } } +void InterpreterOptionsWidget::generateKit() +{ + const QModelIndex &index = m_view->currentIndex(); + if (index.isValid()) + PythonSettings::addKitsForInterpreter(m_model.itemAt(index.row())->itemData); +} + void InterpreterOptionsWidget::cleanUp() { m_model.destroyItems( @@ -563,6 +583,7 @@ void InterpreterOptionsWidget::cleanUp() constexpr char settingsGroupKey[] = "Python"; constexpr char interpreterKey[] = "Interpeter"; constexpr char defaultKey[] = "DefaultInterpeter"; +constexpr char kitsGeneratedKey[] = "KitsGenerated"; constexpr char pylsEnabledKey[] = "PylsEnabled"; constexpr char pylsConfigurationKey[] = "PylsConfiguration"; @@ -786,12 +807,65 @@ PythonSettings::~PythonSettings() settingsInstance = nullptr; } +static void setRelevantAspectsToKit(Kit *k) +{ + QTC_ASSERT(k, return); + QSet relevantAspects = k->relevantAspects(); + relevantAspects.unite({PythonKitAspect::id(), EnvironmentKitAspect::id()}); + k->setRelevantAspects(relevantAspects); +} + +void PythonSettings::addKitsForInterpreter(const Interpreter &interpreter) +{ + if (!KitManager::isLoaded()) { + connect(KitManager::instance(), &KitManager::kitsLoaded, settingsInstance, [interpreter]() { + addKitsForInterpreter(interpreter); + }); + return; + } + + const Id kitId = Id::fromString(interpreter.id); + if (Kit *k = KitManager::kit(kitId)) { + setRelevantAspectsToKit(k); + } else { + KitManager::registerKit( + [interpreter](Kit *k) { + k->setAutoDetected(true); + k->setAutoDetectionSource("Python"); + k->setUnexpandedDisplayName(interpreter.name); + setRelevantAspectsToKit(k); + PythonKitAspect::setPython(k, interpreter.id); + }, + kitId); + } +} + +void PythonSettings::removeKitsForInterpreter(const Interpreter &interpreter) +{ + if (!KitManager::isLoaded()) { + connect(KitManager::instance(), &KitManager::kitsLoaded, settingsInstance, [interpreter]() { + removeKitsForInterpreter(interpreter); + }); + return; + } + + if (Kit *k = KitManager::kit(Id::fromString(interpreter.id))) + KitManager::deregisterKit(k); +} + void PythonSettings::setInterpreter(const QList &interpreters, const QString &defaultId) { if (defaultId == settingsInstance->m_defaultInterpreterId && interpreters == settingsInstance->m_interpreters) { return; } + QList toRemove = settingsInstance->m_interpreters; + for (const Interpreter &interpreter : interpreters) { + if (!Utils::eraseOne(toRemove, Utils::equal(&Interpreter::id, interpreter.id))) + addKitsForInterpreter(interpreter); + } + for (const Interpreter &interpreter : toRemove) + removeKitsForInterpreter(interpreter); settingsInstance->m_interpreters = interpreters; settingsInstance->m_defaultInterpreterId = defaultId; saveSettings(); @@ -833,6 +907,7 @@ void PythonSettings::addInterpreter(const Interpreter &interpreter, bool isDefau if (isDefault) settingsInstance->m_defaultInterpreterId = interpreter.id; saveSettings(); + addKitsForInterpreter(interpreter); } Interpreter PythonSettings::addInterpreter(const FilePath &interpreterPath, @@ -981,7 +1056,24 @@ void PythonSettings::initFromSettings(QtcSettings *settings) || interpreter.command.isExecutableFile(); }; - m_interpreters = Utils::filtered(m_interpreters, keepInterpreter); + const auto [valid, outdatedInterpreters] = Utils::partition(m_interpreters, keepInterpreter); + m_interpreters = valid; + + if (!settings->value(kitsGeneratedKey, false).toBool()) { + for (const Interpreter &interpreter : m_interpreters) { + if (interpreter.autoDetected) { + const FilePath &cmd = interpreter.command; + if (cmd.needsDevice() || cmd.parentDir().pathAppended("activate").exists()) + continue; + } + addKitsForInterpreter(interpreter); + } + } else { + fixupPythonKits(); + } + + for (const Interpreter &outdated : outdatedInterpreters) + removeKitsForInterpreter(outdated); m_defaultInterpreterId = settings->value(defaultKey).toString(); @@ -1014,6 +1106,7 @@ void PythonSettings::writeToSettings(QtcSettings *settings) settings->setValue(defaultKey, m_defaultInterpreterId); settings->setValue(pylsConfigurationKey, m_pylsConfiguration); settings->setValue(pylsEnabledKey, m_pylsEnabled); + settings->setValue(kitsGeneratedKey, true); settings->endGroup(); } @@ -1056,6 +1149,22 @@ void PythonSettings::listDetectedPython(const QString &detectionSource, QString logMessage->append(interpreter.name + '\n'); } +void PythonSettings::fixupPythonKits() +{ + if (!KitManager::isLoaded()) { + connect(KitManager::instance(), + &KitManager::kitsLoaded, + settingsInstance, + &PythonSettings::fixupPythonKits, + Qt::UniqueConnection); + return; + } + for (const Interpreter &interpreter : m_interpreters) { + if (auto k = KitManager::kit(Id::fromString(interpreter.id))) + setRelevantAspectsToKit(k); + } +} + void PythonSettings::saveSettings() { QTC_ASSERT(settingsInstance, return); diff --git a/src/plugins/python/pythonsettings.h b/src/plugins/python/pythonsettings.h index 373054094e1..827748ee3b9 100644 --- a/src/plugins/python/pythonsettings.h +++ b/src/plugins/python/pythonsettings.h @@ -42,6 +42,8 @@ public: const std::function)> &callback, const QString &nameSuffix = {}); static QList detectPythonVenvs(const Utils::FilePath &path); + static void addKitsForInterpreter(const Interpreter &interpreter); + static void removeKitsForInterpreter(const Interpreter &interpreter); signals: void interpretersChanged(const QList &interpreters, const QString &defaultId); @@ -57,6 +59,7 @@ public slots: void listDetectedPython(const QString &detectionSource, QString *logMessage); private: + void fixupPythonKits(); void initFromSettings(Utils::QtcSettings *settings); void writeToSettings(Utils::QtcSettings *settings);