From 10ca1c711edcdf20921052158e76e53747f23cc4 Mon Sep 17 00:00:00 2001 From: hjk Date: Thu, 1 Jul 2021 17:59:15 +0200 Subject: [PATCH] Docker: Partially split out kit item autodetection Most of it is independent of the docker device as such, this should be centralized somehow. This here is a first step. Change-Id: If32063559a4c7c6c3cecf1973e1ef1f634e5f8f4 Reviewed-by: Christian Stenger --- src/plugins/docker/dockerdevice.cpp | 184 ++++++++++++++++------------ src/plugins/docker/dockerdevice.h | 18 +++ 2 files changed, 124 insertions(+), 78 deletions(-) diff --git a/src/plugins/docker/dockerdevice.cpp b/src/plugins/docker/dockerdevice.cpp index accf6895c11..b2a7af5c985 100644 --- a/src/plugins/docker/dockerdevice.cpp +++ b/src/plugins/docker/dockerdevice.cpp @@ -232,6 +232,49 @@ class DockerPortsGatheringMethod : public PortsGatheringMethod } }; +class KitDetectorPrivate +{ + Q_DECLARE_TR_FUNCTIONS(ProjectExplorer::KitItemDetector) + +public: + KitDetectorPrivate(KitDetector *parent, const IDevice::ConstPtr &device) + : q(parent), m_device(device) + {} + + void autoDetect(); + void undoAutoDetect() const; + + QList autoDetectQtVersions() const; + QList autoDetectToolChains(); + void autoDetectCMake(); + void autoDetectDebugger(); + + KitDetector *q; + IDevice::ConstPtr m_device; + QString m_sharedId; +}; + +KitDetector::KitDetector(const IDevice::ConstPtr &device) + : d(new KitDetectorPrivate(this, device)) +{} + +KitDetector::~KitDetector() +{ + delete d; +} + +void KitDetector::autoDetect(const QString &sharedId) const +{ + d->m_sharedId = sharedId; + d->autoDetect(); +} + +void KitDetector::undoAutoDetect(const QString &sharedId) const +{ + d->m_sharedId = sharedId; + d->undoAutoDetect(); +} + class DockerDevicePrivate : public QObject { Q_DECLARE_TR_FUNCTIONS(Docker::Internal::DockerDevice) @@ -255,14 +298,6 @@ public: void tryCreateLocalFileAccess(); - void autoDetect(QTextBrowser *log); - void undoAutoDetect(QTextBrowser *log) const; - - QList autoDetectQtVersions(QTextBrowser *log) const; - QList autoDetectToolChains(QTextBrowser *log); - void autoDetectCMake(QTextBrowser *log); - void autoDetectDebugger(QTextBrowser *log); - void fetchSystemEnviroment(); DockerDevice *q; @@ -283,54 +318,59 @@ class DockerDeviceWidget final : public IDeviceWidget public: explicit DockerDeviceWidget(const IDevice::Ptr &device) - : IDeviceWidget(device) + : IDeviceWidget(device), m_kitItemDetector(device) { auto dockerDevice = device.dynamicCast(); QTC_ASSERT(dockerDevice, return); - auto idLabel = new QLabel(tr("Image ID:")); + DockerDeviceData &data = dockerDevice->data(); + + auto idLabel = new QLabel(tr("Image Id:")); m_idLineEdit = new QLineEdit; - m_idLineEdit->setText(dockerDevice->data().imageId); + m_idLineEdit->setText(data.imageId); m_idLineEdit->setEnabled(false); auto repoLabel = new QLabel(tr("Repository:")); m_repoLineEdit = new QLineEdit; - m_repoLineEdit->setText(dockerDevice->data().repo); + m_repoLineEdit->setText(data.repo); m_repoLineEdit->setEnabled(false); m_runAsOutsideUser = new QCheckBox(tr("Run as outside user")); m_runAsOutsideUser->setToolTip(tr("Use user ID and group ID of the user running Qt Creator " "in the Docker container.")); - m_runAsOutsideUser->setChecked(dockerDevice->data().useLocalUidGid); + m_runAsOutsideUser->setChecked(data.useLocalUidGid); m_runAsOutsideUser->setEnabled(HostOsInfo::isLinuxHost()); - connect(m_runAsOutsideUser, &QCheckBox::toggled, this, [dockerDevice](bool on) { - dockerDevice->data().useLocalUidGid = on; + connect(m_runAsOutsideUser, &QCheckBox::toggled, this, [&data](bool on) { + data.useLocalUidGid = on; }); m_pathsLineEdit = new QLineEdit; - m_pathsLineEdit->setText(dockerDevice->data().repo); + m_pathsLineEdit->setText(data.repo); m_pathsLineEdit->setToolTip(tr("Paths in this semi-colon separated list will be " "mapped one-to-one into the Docker container.")); - m_pathsLineEdit->setText(dockerDevice->data().mounts.join(';')); + m_pathsLineEdit->setText(data.mounts.join(';')); - connect(m_pathsLineEdit, &QLineEdit::textChanged, this, [dockerDevice](const QString &text) { - dockerDevice->data().mounts = text.split(';'); + connect(m_pathsLineEdit, &QLineEdit::textChanged, this, [&data](const QString &text) { + data.mounts = text.split(';'); }); auto logView = new QTextBrowser; + connect(&m_kitItemDetector, &KitDetector::logOutput, + logView, &QTextBrowser::append); auto autoDetectButton = new QPushButton(tr("Auto-detect Kit Items")); auto undoAutoDetectButton = new QPushButton(tr("Remove Auto-Detected Kit Items")); - connect(autoDetectButton, &QPushButton::clicked, this, [logView, dockerDevice] { + connect(autoDetectButton, &QPushButton::clicked, this, [this, logView, id = data.id(), dockerDevice] { logView->clear(); - dockerDevice->d->autoDetect(logView); + dockerDevice->tryCreateLocalFileAccess(); + m_kitItemDetector.autoDetect(id); }); - connect(undoAutoDetectButton, &QPushButton::clicked, this, [logView, dockerDevice] { + connect(undoAutoDetectButton, &QPushButton::clicked, this, [this, logView, id = data.id()] { logView->clear(); - dockerDevice->d->undoAutoDetect(logView); + m_kitItemDetector.undoAutoDetect(id); }); using namespace Layouting; @@ -356,6 +396,8 @@ private: QLineEdit *m_repoLineEdit; QCheckBox *m_runAsOutsideUser; QLineEdit *m_pathsLineEdit; + + KitDetector m_kitItemDetector; }; IDeviceWidget *DockerDevice::createWidget() @@ -424,146 +466,132 @@ DockerDeviceData &DockerDevice::data() return d->m_data; } -void DockerDevicePrivate::undoAutoDetect(QTextBrowser *log) const +void KitDetectorPrivate::undoAutoDetect() const { - const QString id = q->id().toString(); - for (Kit *kit : KitManager::kits()) { - if (kit->autoDetectionSource() == id) { - if (log) - log->append(tr("Removing kit: %1").arg(kit->displayName())); + if (kit->autoDetectionSource() == m_sharedId) { + emit q->logOutput(tr("Removing kit: %1").arg(kit->displayName())); KitManager::deregisterKit(kit); } }; for (BaseQtVersion *qtVersion : QtVersionManager::versions()) { - if (qtVersion->autodetectionSource() == id) { - if (log) - log->append(tr("Removing Qt version: %1").arg(qtVersion->displayName())); + if (qtVersion->autodetectionSource() == m_sharedId) { + emit q->logOutput(tr("Removing Qt version: %1").arg(qtVersion->displayName())); QtVersionManager::removeVersion(qtVersion); } }; - if (log) - log->append(tr("Tool chains not removed.")); + emit q->logOutput(tr("Tool chains not removed.")); // for (ToolChain *toolChain : ToolChainManager::toolChains()) { // if (toolChain->autoDetectionSource() == id.toString()) // // FIXME: Implement // }; - if (log) - log->append(tr("Removal of previously auto-detected kit items finished.") + '\n'); + emit q->logOutput(tr("Removal of previously auto-detected kit items finished.") + '\n'); } -QList DockerDevicePrivate::autoDetectQtVersions(QTextBrowser *log) const +QList KitDetectorPrivate::autoDetectQtVersions() const { QList qtVersions; QString error; const QStringList candidates = {"qmake-qt6", "qmake-qt5", "qmake"}; - if (log) - log->append('\n' + tr("Searching Qt installations...")); + emit q->logOutput('\n' + tr("Searching Qt installations...")); for (const QString &candidate : candidates) { - if (log) - log->append(tr("Searching for %1 executable...").arg(candidate)); - const FilePath qmake = q->searchInPath(FilePath::fromString(candidate)); + emit q->logOutput(tr("Searching for %1 executable...").arg(candidate)); + const FilePath qmake = m_device->searchInPath(FilePath::fromString(candidate)); if (qmake.isEmpty()) continue; - BaseQtVersion *qtVersion = QtVersionFactory::createQtVersionFromQMakePath(qmake, false, m_data.id(), &error); + BaseQtVersion *qtVersion = QtVersionFactory::createQtVersionFromQMakePath(qmake, false, m_sharedId, &error); if (!qtVersion) continue; qtVersions.append(qtVersion); QtVersionManager::addVersion(qtVersion); - if (log) - log->append(tr("Found Qt: %1").arg(qtVersion->qmakeCommand().toUserOutput())); + emit q->logOutput(tr("Found Qt: %1").arg(qtVersion->qmakeCommand().toUserOutput())); } - if (qtVersions.isEmpty() && log) - log->append(tr("No Qt installation found.")); + if (qtVersions.isEmpty()) + emit q->logOutput(tr("No Qt installation found.")); return qtVersions; } -QList DockerDevicePrivate::autoDetectToolChains(QTextBrowser *log) +QList KitDetectorPrivate::autoDetectToolChains() { const QList factories = ToolChainFactory::allToolChainFactories(); QList toolChains; QApplication::processEvents(); - log->append('\n' + tr("Searching tool chains...")); + emit q->logOutput('\n' + tr("Searching tool chains...")); for (ToolChainFactory *factory : factories) { - const QList newToolChains = factory->autoDetect(toolChains, q->sharedFromThis()); - log->append(tr("Searching tool chains of type %1").arg(factory->displayName())); + const QList newToolChains = factory->autoDetect(toolChains, m_device.constCast()); + emit q->logOutput(tr("Searching tool chains of type %1").arg(factory->displayName())); for (ToolChain *toolChain : newToolChains) { - log->append(tr("Found tool chain: %1").arg(toolChain->compilerCommand().toUserOutput())); + emit q->logOutput(tr("Found tool chain: %1").arg(toolChain->compilerCommand().toUserOutput())); ToolChainManager::registerToolChain(toolChain); toolChains.append(toolChain); } } - log->append(tr("%1 new tool chains found.").arg(toolChains.size())); + emit q->logOutput(tr("%1 new tool chains found.").arg(toolChains.size())); return toolChains; } -void DockerDevicePrivate::autoDetectCMake(QTextBrowser *log) +void KitDetectorPrivate::autoDetectCMake() { QObject *cmakeManager = ExtensionSystem::PluginManager::getObjectByName("CMakeToolManager"); if (!cmakeManager) return; - log->append('\n' + tr("Searching CMake binary...")); + emit q->logOutput('\n' + tr("Searching CMake binary...")); QString error; const QStringList candidates = {"cmake"}; for (const QString &candidate : candidates) { - const FilePath cmake = q->searchInPath(FilePath::fromString(candidate)); - QTC_CHECK(q->hasLocalFileAccess()); + const FilePath cmake = m_device->searchInPath(FilePath::fromString(candidate)); if (cmake.isExecutableFile()) { - log->append(tr("Found CMake binary: %1").arg(cmake.toUserOutput())); + emit q->logOutput(tr("Found CMake binary: %1").arg(cmake.toUserOutput())); const bool res = QMetaObject::invokeMethod(cmakeManager, "registerCMakeByPath", Q_ARG(Utils::FilePath, cmake), - Q_ARG(QString, m_data.id())); + Q_ARG(QString, m_sharedId)); QTC_CHECK(res); } } } -void DockerDevicePrivate::autoDetectDebugger(QTextBrowser *log) +void KitDetectorPrivate::autoDetectDebugger() { QObject *debuggerPlugin = ExtensionSystem::PluginManager::getObjectByName("DebuggerPlugin"); if (!debuggerPlugin) return; - if (log) - log->append('\n' + tr("Searching debuggers...")); - const FilePath deviceRoot = q->mapToGlobalPath({}); + emit q->logOutput('\n' + tr("Searching debuggers...")); + const FilePath deviceRoot = m_device->mapToGlobalPath({}); const bool res = QMetaObject::invokeMethod(debuggerPlugin, "autoDetectDebuggersForDevice", Q_ARG(Utils::FilePath, deviceRoot)); QTC_CHECK(res); } -void DockerDevicePrivate::autoDetect(QTextBrowser *log) +void KitDetectorPrivate::autoDetect() { QApplication::setOverrideCursor(Qt::WaitCursor); - undoAutoDetect(log); + undoAutoDetect(); - tryCreateLocalFileAccess(); + emit q->logOutput(tr("Starting auto-detection. This will take a while...")); - if (log) - log->append(tr("Starting auto-detection. This will take a while...")); + QList toolChains = autoDetectToolChains(); + QList qtVersions = autoDetectQtVersions(); - QList toolChains = autoDetectToolChains(log); - QList qtVersions = autoDetectQtVersions(log); - - autoDetectCMake(log); - autoDetectDebugger(log); + autoDetectCMake(); + autoDetectDebugger(); const auto initializeKit = [this, toolChains, qtVersions](Kit *k) { k->setAutoDetected(false); - k->setAutoDetectionSource(m_data.id()); + k->setAutoDetectionSource(m_sharedId); k->setUnexpandedDisplayName("%{Device:Name}"); DeviceTypeKitAspect::setDeviceTypeId(k, Constants::DOCKER_DEVICE_TYPE); - DeviceKitAspect::setDevice(k, q->sharedFromThis()); + DeviceKitAspect::setDevice(k, m_device); for (ToolChain *tc : toolChains) ToolChainKitAspect::setToolChain(k, tc); if (!qtVersions.isEmpty()) @@ -576,8 +604,7 @@ void DockerDevicePrivate::autoDetect(QTextBrowser *log) }; Kit *kit = KitManager::registerKit(initializeKit); - if (log) - log->append('\n' + tr("Registered kit %1").arg(kit->displayName())); + emit q->logOutput('\n' + tr("Registered kit %1").arg(kit->displayName())); QApplication::restoreOverrideCursor(); } @@ -1116,7 +1143,8 @@ Environment DockerDevice::systemEnvironment() const void DockerDevice::aboutToBeRemoved() const { - d->undoAutoDetect(nullptr); + KitDetector detector(sharedFromThis()); + detector.undoAutoDetect(d->m_data.id()); } void DockerDevicePrivate::fetchSystemEnviroment() diff --git a/src/plugins/docker/dockerdevice.h b/src/plugins/docker/dockerdevice.h index 1bed2eb9ce4..509bd2deeeb 100644 --- a/src/plugins/docker/dockerdevice.h +++ b/src/plugins/docker/dockerdevice.h @@ -122,6 +122,24 @@ private: friend class DockerDeviceWidget; }; +class KitDetector : public QObject +{ + Q_OBJECT + +public: + explicit KitDetector(const ProjectExplorer::IDevice::ConstPtr &device); + ~KitDetector() override; + + void autoDetect(const QString &sharedId) const; + void undoAutoDetect(const QString &sharedId) const; + +signals: + void logOutput(const QString &msg); + +private: + class KitDetectorPrivate *d = nullptr; +}; + class DockerDeviceFactory final : public ProjectExplorer::IDeviceFactory { public: