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 <christian.stenger@qt.io>
This commit is contained in:
hjk
2021-07-01 17:59:15 +02:00
parent 5d33982513
commit 10ca1c711e
2 changed files with 124 additions and 78 deletions

View File

@@ -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<BaseQtVersion *> autoDetectQtVersions() const;
QList<ToolChain *> 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 class DockerDevicePrivate : public QObject
{ {
Q_DECLARE_TR_FUNCTIONS(Docker::Internal::DockerDevice) Q_DECLARE_TR_FUNCTIONS(Docker::Internal::DockerDevice)
@@ -255,14 +298,6 @@ public:
void tryCreateLocalFileAccess(); void tryCreateLocalFileAccess();
void autoDetect(QTextBrowser *log);
void undoAutoDetect(QTextBrowser *log) const;
QList<BaseQtVersion *> autoDetectQtVersions(QTextBrowser *log) const;
QList<ToolChain *> autoDetectToolChains(QTextBrowser *log);
void autoDetectCMake(QTextBrowser *log);
void autoDetectDebugger(QTextBrowser *log);
void fetchSystemEnviroment(); void fetchSystemEnviroment();
DockerDevice *q; DockerDevice *q;
@@ -283,54 +318,59 @@ class DockerDeviceWidget final : public IDeviceWidget
public: public:
explicit DockerDeviceWidget(const IDevice::Ptr &device) explicit DockerDeviceWidget(const IDevice::Ptr &device)
: IDeviceWidget(device) : IDeviceWidget(device), m_kitItemDetector(device)
{ {
auto dockerDevice = device.dynamicCast<DockerDevice>(); auto dockerDevice = device.dynamicCast<DockerDevice>();
QTC_ASSERT(dockerDevice, return); 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 = new QLineEdit;
m_idLineEdit->setText(dockerDevice->data().imageId); m_idLineEdit->setText(data.imageId);
m_idLineEdit->setEnabled(false); m_idLineEdit->setEnabled(false);
auto repoLabel = new QLabel(tr("Repository:")); auto repoLabel = new QLabel(tr("Repository:"));
m_repoLineEdit = new QLineEdit; m_repoLineEdit = new QLineEdit;
m_repoLineEdit->setText(dockerDevice->data().repo); m_repoLineEdit->setText(data.repo);
m_repoLineEdit->setEnabled(false); m_repoLineEdit->setEnabled(false);
m_runAsOutsideUser = new QCheckBox(tr("Run as outside user")); 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 " m_runAsOutsideUser->setToolTip(tr("Use user ID and group ID of the user running Qt Creator "
"in the Docker container.")); "in the Docker container."));
m_runAsOutsideUser->setChecked(dockerDevice->data().useLocalUidGid); m_runAsOutsideUser->setChecked(data.useLocalUidGid);
m_runAsOutsideUser->setEnabled(HostOsInfo::isLinuxHost()); m_runAsOutsideUser->setEnabled(HostOsInfo::isLinuxHost());
connect(m_runAsOutsideUser, &QCheckBox::toggled, this, [dockerDevice](bool on) { connect(m_runAsOutsideUser, &QCheckBox::toggled, this, [&data](bool on) {
dockerDevice->data().useLocalUidGid = on; data.useLocalUidGid = on;
}); });
m_pathsLineEdit = new QLineEdit; 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 " m_pathsLineEdit->setToolTip(tr("Paths in this semi-colon separated list will be "
"mapped one-to-one into the Docker container.")); "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) { connect(m_pathsLineEdit, &QLineEdit::textChanged, this, [&data](const QString &text) {
dockerDevice->data().mounts = text.split(';'); data.mounts = text.split(';');
}); });
auto logView = new QTextBrowser; auto logView = new QTextBrowser;
connect(&m_kitItemDetector, &KitDetector::logOutput,
logView, &QTextBrowser::append);
auto autoDetectButton = new QPushButton(tr("Auto-detect Kit Items")); auto autoDetectButton = new QPushButton(tr("Auto-detect Kit Items"));
auto undoAutoDetectButton = new QPushButton(tr("Remove Auto-Detected 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(); 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(); logView->clear();
dockerDevice->d->undoAutoDetect(logView); m_kitItemDetector.undoAutoDetect(id);
}); });
using namespace Layouting; using namespace Layouting;
@@ -356,6 +396,8 @@ private:
QLineEdit *m_repoLineEdit; QLineEdit *m_repoLineEdit;
QCheckBox *m_runAsOutsideUser; QCheckBox *m_runAsOutsideUser;
QLineEdit *m_pathsLineEdit; QLineEdit *m_pathsLineEdit;
KitDetector m_kitItemDetector;
}; };
IDeviceWidget *DockerDevice::createWidget() IDeviceWidget *DockerDevice::createWidget()
@@ -424,146 +466,132 @@ DockerDeviceData &DockerDevice::data()
return d->m_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()) { for (Kit *kit : KitManager::kits()) {
if (kit->autoDetectionSource() == id) { if (kit->autoDetectionSource() == m_sharedId) {
if (log) emit q->logOutput(tr("Removing kit: %1").arg(kit->displayName()));
log->append(tr("Removing kit: %1").arg(kit->displayName()));
KitManager::deregisterKit(kit); KitManager::deregisterKit(kit);
} }
}; };
for (BaseQtVersion *qtVersion : QtVersionManager::versions()) { for (BaseQtVersion *qtVersion : QtVersionManager::versions()) {
if (qtVersion->autodetectionSource() == id) { if (qtVersion->autodetectionSource() == m_sharedId) {
if (log) emit q->logOutput(tr("Removing Qt version: %1").arg(qtVersion->displayName()));
log->append(tr("Removing Qt version: %1").arg(qtVersion->displayName()));
QtVersionManager::removeVersion(qtVersion); QtVersionManager::removeVersion(qtVersion);
} }
}; };
if (log) emit q->logOutput(tr("Tool chains not removed."));
log->append(tr("Tool chains not removed."));
// for (ToolChain *toolChain : ToolChainManager::toolChains()) { // for (ToolChain *toolChain : ToolChainManager::toolChains()) {
// if (toolChain->autoDetectionSource() == id.toString()) // if (toolChain->autoDetectionSource() == id.toString())
// // FIXME: Implement // // FIXME: Implement
// }; // };
if (log) emit q->logOutput(tr("Removal of previously auto-detected kit items finished.") + '\n');
log->append(tr("Removal of previously auto-detected kit items finished.") + '\n');
} }
QList<BaseQtVersion *> DockerDevicePrivate::autoDetectQtVersions(QTextBrowser *log) const QList<BaseQtVersion *> KitDetectorPrivate::autoDetectQtVersions() const
{ {
QList<BaseQtVersion *> qtVersions; QList<BaseQtVersion *> qtVersions;
QString error; QString error;
const QStringList candidates = {"qmake-qt6", "qmake-qt5", "qmake"}; const QStringList candidates = {"qmake-qt6", "qmake-qt5", "qmake"};
if (log) emit q->logOutput('\n' + tr("Searching Qt installations..."));
log->append('\n' + tr("Searching Qt installations..."));
for (const QString &candidate : candidates) { for (const QString &candidate : candidates) {
if (log) emit q->logOutput(tr("Searching for %1 executable...").arg(candidate));
log->append(tr("Searching for %1 executable...").arg(candidate)); const FilePath qmake = m_device->searchInPath(FilePath::fromString(candidate));
const FilePath qmake = q->searchInPath(FilePath::fromString(candidate));
if (qmake.isEmpty()) if (qmake.isEmpty())
continue; continue;
BaseQtVersion *qtVersion = QtVersionFactory::createQtVersionFromQMakePath(qmake, false, m_data.id(), &error); BaseQtVersion *qtVersion = QtVersionFactory::createQtVersionFromQMakePath(qmake, false, m_sharedId, &error);
if (!qtVersion) if (!qtVersion)
continue; continue;
qtVersions.append(qtVersion); qtVersions.append(qtVersion);
QtVersionManager::addVersion(qtVersion); QtVersionManager::addVersion(qtVersion);
if (log) emit q->logOutput(tr("Found Qt: %1").arg(qtVersion->qmakeCommand().toUserOutput()));
log->append(tr("Found Qt: %1").arg(qtVersion->qmakeCommand().toUserOutput()));
} }
if (qtVersions.isEmpty() && log) if (qtVersions.isEmpty())
log->append(tr("No Qt installation found.")); emit q->logOutput(tr("No Qt installation found."));
return qtVersions; return qtVersions;
} }
QList<ToolChain *> DockerDevicePrivate::autoDetectToolChains(QTextBrowser *log) QList<ToolChain *> KitDetectorPrivate::autoDetectToolChains()
{ {
const QList<ToolChainFactory *> factories = ToolChainFactory::allToolChainFactories(); const QList<ToolChainFactory *> factories = ToolChainFactory::allToolChainFactories();
QList<ToolChain *> toolChains; QList<ToolChain *> toolChains;
QApplication::processEvents(); QApplication::processEvents();
log->append('\n' + tr("Searching tool chains...")); emit q->logOutput('\n' + tr("Searching tool chains..."));
for (ToolChainFactory *factory : factories) { for (ToolChainFactory *factory : factories) {
const QList<ToolChain *> newToolChains = factory->autoDetect(toolChains, q->sharedFromThis()); const QList<ToolChain *> newToolChains = factory->autoDetect(toolChains, m_device.constCast<IDevice>());
log->append(tr("Searching tool chains of type %1").arg(factory->displayName())); emit q->logOutput(tr("Searching tool chains of type %1").arg(factory->displayName()));
for (ToolChain *toolChain : newToolChains) { 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); ToolChainManager::registerToolChain(toolChain);
toolChains.append(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; return toolChains;
} }
void DockerDevicePrivate::autoDetectCMake(QTextBrowser *log) void KitDetectorPrivate::autoDetectCMake()
{ {
QObject *cmakeManager = ExtensionSystem::PluginManager::getObjectByName("CMakeToolManager"); QObject *cmakeManager = ExtensionSystem::PluginManager::getObjectByName("CMakeToolManager");
if (!cmakeManager) if (!cmakeManager)
return; return;
log->append('\n' + tr("Searching CMake binary...")); emit q->logOutput('\n' + tr("Searching CMake binary..."));
QString error; QString error;
const QStringList candidates = {"cmake"}; const QStringList candidates = {"cmake"};
for (const QString &candidate : candidates) { for (const QString &candidate : candidates) {
const FilePath cmake = q->searchInPath(FilePath::fromString(candidate)); const FilePath cmake = m_device->searchInPath(FilePath::fromString(candidate));
QTC_CHECK(q->hasLocalFileAccess());
if (cmake.isExecutableFile()) { 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, const bool res = QMetaObject::invokeMethod(cmakeManager,
"registerCMakeByPath", "registerCMakeByPath",
Q_ARG(Utils::FilePath, cmake), Q_ARG(Utils::FilePath, cmake),
Q_ARG(QString, m_data.id())); Q_ARG(QString, m_sharedId));
QTC_CHECK(res); QTC_CHECK(res);
} }
} }
} }
void DockerDevicePrivate::autoDetectDebugger(QTextBrowser *log) void KitDetectorPrivate::autoDetectDebugger()
{ {
QObject *debuggerPlugin = ExtensionSystem::PluginManager::getObjectByName("DebuggerPlugin"); QObject *debuggerPlugin = ExtensionSystem::PluginManager::getObjectByName("DebuggerPlugin");
if (!debuggerPlugin) if (!debuggerPlugin)
return; return;
if (log) emit q->logOutput('\n' + tr("Searching debuggers..."));
log->append('\n' + tr("Searching debuggers...")); const FilePath deviceRoot = m_device->mapToGlobalPath({});
const FilePath deviceRoot = q->mapToGlobalPath({});
const bool res = QMetaObject::invokeMethod(debuggerPlugin, const bool res = QMetaObject::invokeMethod(debuggerPlugin,
"autoDetectDebuggersForDevice", "autoDetectDebuggersForDevice",
Q_ARG(Utils::FilePath, deviceRoot)); Q_ARG(Utils::FilePath, deviceRoot));
QTC_CHECK(res); QTC_CHECK(res);
} }
void DockerDevicePrivate::autoDetect(QTextBrowser *log) void KitDetectorPrivate::autoDetect()
{ {
QApplication::setOverrideCursor(Qt::WaitCursor); QApplication::setOverrideCursor(Qt::WaitCursor);
undoAutoDetect(log); undoAutoDetect();
tryCreateLocalFileAccess(); emit q->logOutput(tr("Starting auto-detection. This will take a while..."));
if (log) QList<ToolChain *> toolChains = autoDetectToolChains();
log->append(tr("Starting auto-detection. This will take a while...")); QList<BaseQtVersion *> qtVersions = autoDetectQtVersions();
QList<ToolChain *> toolChains = autoDetectToolChains(log); autoDetectCMake();
QList<BaseQtVersion *> qtVersions = autoDetectQtVersions(log); autoDetectDebugger();
autoDetectCMake(log);
autoDetectDebugger(log);
const auto initializeKit = [this, toolChains, qtVersions](Kit *k) { const auto initializeKit = [this, toolChains, qtVersions](Kit *k) {
k->setAutoDetected(false); k->setAutoDetected(false);
k->setAutoDetectionSource(m_data.id()); k->setAutoDetectionSource(m_sharedId);
k->setUnexpandedDisplayName("%{Device:Name}"); k->setUnexpandedDisplayName("%{Device:Name}");
DeviceTypeKitAspect::setDeviceTypeId(k, Constants::DOCKER_DEVICE_TYPE); DeviceTypeKitAspect::setDeviceTypeId(k, Constants::DOCKER_DEVICE_TYPE);
DeviceKitAspect::setDevice(k, q->sharedFromThis()); DeviceKitAspect::setDevice(k, m_device);
for (ToolChain *tc : toolChains) for (ToolChain *tc : toolChains)
ToolChainKitAspect::setToolChain(k, tc); ToolChainKitAspect::setToolChain(k, tc);
if (!qtVersions.isEmpty()) if (!qtVersions.isEmpty())
@@ -576,8 +604,7 @@ void DockerDevicePrivate::autoDetect(QTextBrowser *log)
}; };
Kit *kit = KitManager::registerKit(initializeKit); Kit *kit = KitManager::registerKit(initializeKit);
if (log) emit q->logOutput('\n' + tr("Registered kit %1").arg(kit->displayName()));
log->append('\n' + tr("Registered kit %1").arg(kit->displayName()));
QApplication::restoreOverrideCursor(); QApplication::restoreOverrideCursor();
} }
@@ -1116,7 +1143,8 @@ Environment DockerDevice::systemEnvironment() const
void DockerDevice::aboutToBeRemoved() const void DockerDevice::aboutToBeRemoved() const
{ {
d->undoAutoDetect(nullptr); KitDetector detector(sharedFromThis());
detector.undoAutoDetect(d->m_data.id());
} }
void DockerDevicePrivate::fetchSystemEnviroment() void DockerDevicePrivate::fetchSystemEnviroment()

View File

@@ -122,6 +122,24 @@ private:
friend class DockerDeviceWidget; 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 class DockerDeviceFactory final : public ProjectExplorer::IDeviceFactory
{ {
public: public: