Docker: detect python in docker images

Change-Id: I841cdb7ce1cb8f34565a5e6993c5c825937e3eab
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
David Schulz
2022-08-26 15:16:08 +02:00
parent 407082bcb2
commit a634833720
5 changed files with 159 additions and 7 deletions

View File

@@ -110,6 +110,16 @@ public:
return res; return res;
} }
QList<ItemData> allData(const std::function<bool(const ItemData &)> &pred) const
{
QList<ItemData> res;
BaseModel::rootItem()->forFirstLevelChildren([pred, &res](ChildType *item) {
if (pred(item->itemData))
res.append(item->itemData);
});
return res;
}
void setAllData(const QList<ItemData> &items) void setAllData(const QList<ItemData> &items)
{ {
BaseModel::rootItem()->removeChildren(); BaseModel::rootItem()->removeChildren();

View File

@@ -47,6 +47,7 @@ public:
private: private:
QtVersions autoDetectQtVersions() const; QtVersions autoDetectQtVersions() const;
QList<ToolChain *> autoDetectToolChains(); QList<ToolChain *> autoDetectToolChains();
void autoDetectPython();
QList<Id> autoDetectCMake(); QList<Id> autoDetectCMake();
void autoDetectDebugger(); void autoDetectDebugger();
@@ -133,6 +134,16 @@ void KitDetectorPrivate::undoAutoDetect() const
emit q->logOutput('\n' + logMessage); emit q->logOutput('\n' + logMessage);
} }
if (auto pythonSettings = ExtensionSystem::PluginManager::getObjectByName("PythonSettings")) {
QString logMessage;
const bool res = QMetaObject::invokeMethod(pythonSettings,
"removeDetectedPython",
Q_ARG(QString, m_sharedId),
Q_ARG(QString *, &logMessage));
QTC_CHECK(res);
emit q->logOutput('\n' + logMessage);
}
emit q->logOutput('\n' + ProjectExplorer::Tr::tr("Removal of previously auto-detected kit items finished.") + "\n\n"); emit q->logOutput('\n' + ProjectExplorer::Tr::tr("Removal of previously auto-detected kit items finished.") + "\n\n");
} }
@@ -180,6 +191,16 @@ void KitDetectorPrivate::listAutoDetected() const
emit q->logOutput('\n' + logMessage); emit q->logOutput('\n' + logMessage);
} }
if (auto pythonSettings = ExtensionSystem::PluginManager::getObjectByName("PythonSettings")) {
QString logMessage;
const bool res = QMetaObject::invokeMethod(pythonSettings,
"listDetectedPython",
Q_ARG(QString, m_sharedId),
Q_ARG(QString *, &logMessage));
QTC_CHECK(res);
emit q->logOutput('\n' + logMessage);
}
emit q->logOutput('\n' + ProjectExplorer::Tr::tr("Listing of previously auto-detected kit items finished.") + "\n\n"); emit q->logOutput('\n' + ProjectExplorer::Tr::tr("Listing of previously auto-detected kit items finished.") + "\n\n");
} }
@@ -252,6 +273,23 @@ Toolchains KitDetectorPrivate::autoDetectToolChains()
return allNewToolChains; return allNewToolChains;
} }
void KitDetectorPrivate::autoDetectPython()
{
QObject *pythonSettings = ExtensionSystem::PluginManager::getObjectByName("PythonSettings");
if (!pythonSettings)
return;
QString logMessage;
const bool res = QMetaObject::invokeMethod(pythonSettings,
"detectPythonOnDevice",
Q_ARG(Utils::FilePaths, m_searchPaths),
Q_ARG(QString, m_device->displayName()),
Q_ARG(QString, m_sharedId),
Q_ARG(QString *, &logMessage));
QTC_CHECK(res);
emit q->logOutput('\n' + logMessage);
}
QList<Id> KitDetectorPrivate::autoDetectCMake() QList<Id> KitDetectorPrivate::autoDetectCMake()
{ {
QList<Id> result; QList<Id> result;
@@ -302,6 +340,7 @@ void KitDetectorPrivate::autoDetect()
const QList<Id> cmakeIds = autoDetectCMake(); const QList<Id> cmakeIds = autoDetectCMake();
const Id cmakeId = cmakeIds.empty() ? Id() : cmakeIds.first(); const Id cmakeId = cmakeIds.empty() ? Id() : cmakeIds.first();
autoDetectDebugger(); autoDetectDebugger();
autoDetectPython();
const auto initializeKit = [this, toolchains, qtVersions, cmakeId](Kit *k) { const auto initializeKit = [this, toolchains, qtVersions, cmakeId](Kit *k) {
k->setAutoDetected(false); k->setAutoDetected(false);

View File

@@ -209,13 +209,15 @@ public:
inline bool operator==(const Interpreter &other) const inline bool operator==(const Interpreter &other) const
{ {
return id == other.id && name == other.name && command == other.command; return id == other.id && name == other.name && command == other.command
&& detectionSource == other.detectionSource;
} }
QString id; QString id;
QString name; QString name;
Utils::FilePath command; Utils::FilePath command;
bool autoDetected = true; bool autoDetected = true;
QString detectionSource;
}; };
class PROJECTEXPLORER_EXPORT InterpreterAspect : public Utils::BaseAspect class PROJECTEXPLORER_EXPORT InterpreterAspect : public Utils::BaseAspect

View File

@@ -52,7 +52,7 @@ namespace Python::Internal {
static Interpreter createInterpreter(const FilePath &python, static Interpreter createInterpreter(const FilePath &python,
const QString &defaultName, const QString &defaultName,
bool windowedSuffix = false) const QString &suffix = {})
{ {
Interpreter result; Interpreter result;
result.id = QUuid::createUuid().toString(); result.id = QUuid::createUuid().toString();
@@ -67,11 +67,11 @@ static Interpreter createInterpreter(const FilePath &python,
result.name = pythonProcess.cleanedStdOut().trimmed(); result.name = pythonProcess.cleanedStdOut().trimmed();
if (result.name.isEmpty()) if (result.name.isEmpty())
result.name = defaultName; result.name = defaultName;
if (windowedSuffix)
result.name += " (Windowed)";
QDir pythonDir(python.parentDir().toString()); QDir pythonDir(python.parentDir().toString());
if (pythonDir.exists() && pythonDir.exists("activate") && pythonDir.cdUp()) if (pythonDir.exists() && pythonDir.exists("activate") && pythonDir.cdUp())
result.name += QString(" (%1 Virtual Environment)").arg(pythonDir.dirName()); result.name += QString(" (%1 Virtual Environment)").arg(pythonDir.dirName());
if (!suffix.isEmpty())
result.name += ' ' + suffix;
return result; return result;
} }
@@ -125,6 +125,11 @@ public:
void apply() override; void apply() override;
void addInterpreter(const Interpreter &interpreter);
void removeInterpreterFrom(const QString &detectionSource);
QList<Interpreter> interpreters() const;
QList<Interpreter> interpreterFrom(const QString &detectionSource) const;
private: private:
QTreeView m_view; QTreeView m_view;
ListModel<Interpreter> m_model; ListModel<Interpreter> m_model;
@@ -218,11 +223,31 @@ InterpreterOptionsWidget::InterpreterOptionsWidget()
} }
void InterpreterOptionsWidget::apply() void InterpreterOptionsWidget::apply()
{
PythonSettings::setInterpreter(interpreters(), m_defaultId);
}
void InterpreterOptionsWidget::addInterpreter(const Interpreter &interpreter)
{
m_model.appendItem(interpreter);
}
void InterpreterOptionsWidget::removeInterpreterFrom(const QString &detectionSource)
{
m_model.destroyItems(Utils::equal(&Interpreter::detectionSource, detectionSource));
}
QList<Interpreter> InterpreterOptionsWidget::interpreters() const
{ {
QList<Interpreter> interpreters; QList<Interpreter> interpreters;
for (const TreeItem *treeItem : m_model) for (const TreeItem *treeItem : m_model)
interpreters << static_cast<const ListItem<Interpreter> *>(treeItem)->itemData; interpreters << static_cast<const ListItem<Interpreter> *>(treeItem)->itemData;
PythonSettings::setInterpreter(interpreters, m_defaultId); return interpreters;
}
QList<Interpreter> InterpreterOptionsWidget::interpreterFrom(const QString &detectionSource) const
{
return m_model.allData(Utils::equal(&Interpreter::detectionSource, detectionSource));
} }
void InterpreterOptionsWidget::currentChanged(const QModelIndex &index, const QModelIndex &previous) void InterpreterOptionsWidget::currentChanged(const QModelIndex &index, const QModelIndex &previous)
@@ -287,6 +312,32 @@ public:
setCategoryIconPath(":/python/images/settingscategory_python.png"); setCategoryIconPath(":/python/images/settingscategory_python.png");
setWidgetCreator([]() { return new InterpreterOptionsWidget(); }); setWidgetCreator([]() { return new InterpreterOptionsWidget(); });
} }
QList<Interpreter> interpreters()
{
if (auto w = static_cast<InterpreterOptionsWidget *>(widget()))
return w->interpreters();
return {};
}
void addInterpreter(const Interpreter &interpreter)
{
if (auto w = static_cast<InterpreterOptionsWidget *>(widget()))
w->addInterpreter(interpreter);
}
void removeInterpreterFrom(const QString &detectionSource)
{
if (auto w = static_cast<InterpreterOptionsWidget *>(widget()))
w->removeInterpreterFrom(detectionSource);
}
QList<Interpreter> interpreterFrom(const QString &detectionSource)
{
if (auto w = static_cast<InterpreterOptionsWidget *>(widget()))
return w->interpreterFrom(detectionSource);
return {};
}
}; };
static bool alreadyRegistered(const QList<Interpreter> &pythons, const FilePath &pythonExecutable) static bool alreadyRegistered(const QList<Interpreter> &pythons, const FilePath &pythonExecutable)
@@ -582,7 +633,7 @@ static void addPythonsFromRegistry(QList<Interpreter> &pythons)
pythons << createInterpreter(python, "Python " + versionGroup); pythons << createInterpreter(python, "Python " + versionGroup);
const FilePath pythonw = path.pathAppended("pythonw").withExecutableSuffix(); const FilePath pythonw = path.pathAppended("pythonw").withExecutableSuffix();
if (pythonw.exists() && !alreadyRegistered(pythons, pythonw)) if (pythonw.exists() && !alreadyRegistered(pythons, pythonw))
pythons << createInterpreter(pythonw, "Python " + versionGroup, true); pythons << createInterpreter(pythonw, "Python " + versionGroup, "(Windowed)");
} }
pythonRegistry.endGroup(); pythonRegistry.endGroup();
} }
@@ -602,7 +653,7 @@ static void addPythonsFromPath(QList<Interpreter> &pythons)
} }
for (const FilePath &executable : env.findAllInPath("pythonw")) { for (const FilePath &executable : env.findAllInPath("pythonw")) {
if (executable.exists() && !alreadyRegistered(pythons, executable)) if (executable.exists() && !alreadyRegistered(pythons, executable))
pythons << createInterpreter(executable, "Python from Path", true); pythons << createInterpreter(executable, "Python from Path", "(Windowed)");
} }
} else { } else {
const QStringList filters = {"python", const QStringList filters = {"python",
@@ -639,6 +690,9 @@ static PythonSettings *settingsInstance = nullptr;
PythonSettings::PythonSettings() PythonSettings::PythonSettings()
: QObject(PythonPlugin::instance()) : QObject(PythonPlugin::instance())
{ {
setObjectName("PythonSettings");
ExtensionSystem::PluginManager::addObject(this);
initFromSettings(Core::ICore::settings()); initFromSettings(Core::ICore::settings());
if (HostOsInfo::isWindowsHost()) if (HostOsInfo::isWindowsHost())
@@ -813,6 +867,45 @@ void PythonSettings::writeToSettings(QSettings *settings)
settings->endGroup(); settings->endGroup();
} }
void PythonSettings::detectPythonOnDevice(const Utils::FilePaths &searchPaths,
const QString &deviceName,
const QString &detectionSource,
QString *logMessage)
{
QStringList messages{tr("Searching Python binaries...")};
auto alreadyConfigured = interpreterOptionsPage().interpreters();
for (const FilePath &path : searchPaths) {
const FilePath python = path.pathAppended("python3").withExecutableSuffix();
if (!python.isExecutableFile())
continue;
if (Utils::contains(alreadyConfigured, Utils::equal(&Interpreter::command, python)))
continue;
auto interpreter = createInterpreter(python, "Python on", "on " + deviceName);
interpreter.detectionSource = detectionSource;
interpreterOptionsPage().addInterpreter(interpreter);
messages.append(tr("Found \"%1\" (%2)").arg(interpreter.name, python.toUserOutput()));
}
if (logMessage)
*logMessage = messages.join('\n');
}
void PythonSettings::removeDetectedPython(const QString &detectionSource, QString *logMessage)
{
if (logMessage)
logMessage->append(Tr::tr("Removing Python") + '\n');
interpreterOptionsPage().removeInterpreterFrom(detectionSource);
}
void PythonSettings::listDetectedPython(const QString &detectionSource, QString *logMessage)
{
if (!logMessage)
return;
logMessage->append(Tr::tr("Python:") + '\n');
for (Interpreter &interpreter: interpreterOptionsPage().interpreterFrom(detectionSource))
logMessage->append(interpreter.name + '\n');
}
void PythonSettings::saveSettings() void PythonSettings::saveSettings()
{ {
QTC_ASSERT(settingsInstance, return); QTC_ASSERT(settingsInstance, return);

View File

@@ -36,6 +36,14 @@ signals:
void pylsConfigurationChanged(const QString &configuration); void pylsConfigurationChanged(const QString &configuration);
void pylsEnabledChanged(const bool enabled); void pylsEnabledChanged(const bool enabled);
public slots:
void detectPythonOnDevice(const Utils::FilePaths &searchPaths,
const QString &deviceName,
const QString &detectionSource,
QString *logMessage);
void removeDetectedPython(const QString &detectionSource, QString *logMessage);
void listDetectedPython(const QString &detectionSource, QString *logMessage);
private: private:
PythonSettings(); PythonSettings();