docker-plugin: Split out dockerdevice utility classes

Moved DockerDeviceWidget and KitDetector into their own files

Change-Id: I16d52a4f27f611b6278e2144c4718bd370f99b63
Reviewed-by: hjk <hjk@qt.io>
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
Marcus Tillmanns
2022-04-06 10:15:29 +02:00
parent e7a0c380d7
commit 0ee47fc734
8 changed files with 696 additions and 507 deletions

View File

@@ -5,6 +5,8 @@ add_qtc_plugin(Docker
dockerbuildstep.cpp dockerbuildstep.h
dockerconstants.h
dockerdevice.cpp dockerdevice.h
dockerdevicewidget.cpp dockerdevicewidget.h
dockerplugin.cpp dockerplugin.h
dockersettings.cpp dockersettings.h
kitdetector.cpp kitdetector.h
)

View File

@@ -12,15 +12,19 @@ QtcPlugin {
files: [
"docker_global.h",
"dockerbuildstep.h",
"dockerbuildstep.cpp",
"dockerbuildstep.h",
"dockerconstants.h",
"dockerdevice.h",
"dockerdevice.cpp",
"dockerplugin.h",
"dockerdevice.h",
"dockerdevicewidget.cpp",
"dockerdevicewidget.h",
"dockerplugin.cpp",
"dockerplugin.h",
"dockersettings.cpp",
"dockersettings.h",
"dockersettings.cpp"
"kitdetector.cpp",
"kitdetector.h",
]
}

View File

@@ -27,6 +27,8 @@
#include "dockerconstants.h"
#include "dockerplugin.h"
#include "dockerdevicewidget.h"
#include "kitdetector.h"
#include <extensionsystem/pluginmanager.h>
@@ -185,61 +187,7 @@ 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;
void listAutoDetected() const;
void setSharedId(const QString &sharedId) { m_sharedId = sharedId; }
void setSearchPaths(const FilePaths &searchPaths) { m_searchPaths = searchPaths; }
private:
QtVersions autoDetectQtVersions() const;
QList<ToolChain *> autoDetectToolChains();
void autoDetectCMake();
void autoDetectDebugger();
KitDetector *q;
IDevice::ConstPtr m_device;
QString m_sharedId;
FilePaths m_searchPaths;
};
KitDetector::KitDetector(const IDevice::ConstPtr &device)
: d(new KitDetectorPrivate(this, device))
{}
KitDetector::~KitDetector()
{
delete d;
}
void KitDetector::autoDetect(const QString &sharedId, const FilePaths &searchPaths) const
{
d->setSharedId(sharedId);
d->setSearchPaths(searchPaths);
d->autoDetect();
}
void KitDetector::undoAutoDetect(const QString &sharedId) const
{
d->setSharedId(sharedId);
d->undoAutoDetect();
}
void KitDetector::listAutoDetected(const QString &sharedId) const
{
d->setSharedId(sharedId);
d->listAutoDetected();
}
class DockerDevicePrivate : public QObject
{
@@ -274,191 +222,6 @@ public:
bool m_useFind = true; // prefer find over ls and hacks, but be able to use ls as fallback
};
class DockerDeviceWidget final : public IDeviceWidget
{
Q_DECLARE_TR_FUNCTIONS(Docker::Internal::DockerDevice)
public:
explicit DockerDeviceWidget(const IDevice::Ptr &device)
: IDeviceWidget(device), m_kitItemDetector(device)
{
auto dockerDevice = device.dynamicCast<DockerDevice>();
QTC_ASSERT(dockerDevice, return);
DockerDeviceData &data = dockerDevice->data();
auto repoLabel = new QLabel(tr("Repository:"));
m_repoLineEdit = new QLineEdit;
m_repoLineEdit->setText(data.repo);
m_repoLineEdit->setEnabled(false);
auto tagLabel = new QLabel(tr("Tag:"));
m_tagLineEdit = new QLineEdit;
m_tagLineEdit->setText(data.tag);
m_tagLineEdit->setEnabled(false);
auto idLabel = new QLabel(tr("Image ID:"));
m_idLineEdit = new QLineEdit;
m_idLineEdit->setText(data.imageId);
m_idLineEdit->setEnabled(false);
auto daemonStateLabel = new QLabel(tr("Daemon state:"));
m_daemonReset = new QToolButton;
m_daemonReset->setToolTip(tr("Clears detected daemon state. "
"It will be automatically re-evaluated next time access is needed."));
m_daemonState = new QLabel;
updateDaemonStateTexts();
connect(m_daemonReset, &QToolButton::clicked, this, [this, dockerDevice] {
DockerPlugin::setGlobalDaemonState(Utils::nullopt);
updateDaemonStateTexts();
});
m_runAsOutsideUser = new QCheckBox(tr("Run as outside user"));
m_runAsOutsideUser->setToolTip(tr("Uses user ID and group ID of the user running Qt Creator "
"in the docker container."));
m_runAsOutsideUser->setChecked(data.useLocalUidGid);
m_runAsOutsideUser->setEnabled(HostOsInfo::isLinuxHost());
connect(m_runAsOutsideUser, &QCheckBox::toggled, this, [&data](bool on) {
data.useLocalUidGid = on;
});
m_pathsListLabel = new InfoLabel(tr("Paths to mount:"));
// FIXME: 8.0: use
//m_pathsListLabel->setToolTip(tr("Source directory list should not be empty"));
m_pathsListEdit = new PathListEditor;
// FIXME: 8.0: use
//m_pathsListEdit->setPlaceholderText(tr("Host directories to mount into the container"));
m_pathsListEdit->setToolTip(tr("Maps paths in this list one-to-one to the "
"docker container."));
m_pathsListEdit->setPathList(data.mounts);
auto markupMounts = [this] {
const bool isEmpty = m_pathsListEdit->pathList().isEmpty();
m_pathsListLabel->setType(isEmpty ? InfoLabel::Warning : InfoLabel::None);
};
markupMounts();
connect(m_pathsListEdit, &PathListEditor::changed, this, [dockerDevice, markupMounts, this] {
dockerDevice->setMounts(m_pathsListEdit->pathList());
markupMounts();
});
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"));
auto listAutoDetectedButton = new QPushButton(tr("List Auto-Detected Kit Items"));
auto searchDirsComboBox = new QComboBox;
searchDirsComboBox->addItem(tr("Search in PATH"));
searchDirsComboBox->addItem(tr("Search in Selected Directories"));
auto searchDirsLineEdit = new FancyLineEdit;
// FIXME: 8.0: use
//searchDirsLineEdit->setPlaceholderText(tr("Semicolon-separated list of directories"));
searchDirsLineEdit->setToolTip(
tr("Select the paths in the docker image that should be scanned for kit entries."));
searchDirsLineEdit->setHistoryCompleter("DockerMounts", true);
auto searchPaths = [searchDirsComboBox, searchDirsLineEdit, dockerDevice] {
FilePaths paths;
if (searchDirsComboBox->currentIndex() == 0) {
paths = dockerDevice->systemEnvironment().path();
} else {
for (const QString &path : searchDirsLineEdit->text().split(';'))
paths.append(FilePath::fromString(path.trimmed()));
}
paths = Utils::transform(paths, [dockerDevice](const FilePath &path) {
return dockerDevice->mapToGlobalPath(path);
});
return paths;
};
connect(autoDetectButton, &QPushButton::clicked, this,
[this, logView, dockerDevice, searchPaths] {
logView->clear();
dockerDevice->updateContainerAccess();
m_kitItemDetector.autoDetect(dockerDevice->id().toString(), searchPaths());
if (DockerPlugin::isDaemonRunning().value_or(false) == false)
logView->append(tr("Docker daemon appears to be not running."));
else
logView->append(tr("Docker daemon appears to be running."));
updateDaemonStateTexts();
});
connect(undoAutoDetectButton, &QPushButton::clicked, this, [this, logView, device] {
logView->clear();
m_kitItemDetector.undoAutoDetect(device->id().toString());
});
connect(listAutoDetectedButton, &QPushButton::clicked, this, [this, logView, device] {
logView->clear();
m_kitItemDetector.listAutoDetected(device->id().toString());
});
using namespace Layouting;
Form {
repoLabel, m_repoLineEdit, Break(),
tagLabel, m_tagLineEdit, Break(),
idLabel, m_idLineEdit, Break(),
daemonStateLabel, m_daemonReset, m_daemonState, Break(),
m_runAsOutsideUser, Break(),
Column {
m_pathsListLabel,
m_pathsListEdit,
}, Break(),
Column {
Space(20),
Row {
searchDirsComboBox,
searchDirsLineEdit
},
Row {
autoDetectButton,
undoAutoDetectButton,
listAutoDetectedButton,
Stretch(),
},
new QLabel(tr("Detection log:")),
logView
}
}.attachTo(this);
searchDirsLineEdit->setVisible(false);
auto updateDirectoriesLineEdit = [searchDirsLineEdit](int index) {
searchDirsLineEdit->setVisible(index == 1);
if (index == 1)
searchDirsLineEdit->setFocus();
};
QObject::connect(searchDirsComboBox, qOverload<int>(&QComboBox::activated),
this, updateDirectoriesLineEdit);
}
void updateDeviceFromUi() final {}
void updateDaemonStateTexts();
private:
QLineEdit *m_repoLineEdit;
QLineEdit *m_tagLineEdit;
QLineEdit *m_idLineEdit;
QToolButton *m_daemonReset;
QLabel *m_daemonState;
QCheckBox *m_runAsOutsideUser;
InfoLabel *m_pathsListLabel;
PathListEditor *m_pathsListEdit;
KitDetector m_kitItemDetector;
};
IDeviceWidget *DockerDevice::createWidget()
{
return new DockerDeviceWidget(sharedFromThis());
@@ -476,7 +239,6 @@ Tasks DockerDevice::validate() const
return result;
}
// DockerDeviceData
QString DockerDeviceData::repoAndTag() const
@@ -547,236 +309,7 @@ DockerDeviceData &DockerDevice::data()
return d->m_data;
}
void KitDetectorPrivate::undoAutoDetect() const
{
emit q->logOutput(tr("Start removing auto-detected items associated with this docker image."));
emit q->logOutput('\n' + tr("Removing kits..."));
for (Kit *kit : KitManager::kits()) {
if (kit->autoDetectionSource() == m_sharedId) {
emit q->logOutput(tr("Removed \"%1\"").arg(kit->displayName()));
KitManager::deregisterKit(kit);
}
};
emit q->logOutput('\n' + tr("Removing Qt version entries..."));
for (QtVersion *qtVersion : QtVersionManager::versions()) {
if (qtVersion->detectionSource() == m_sharedId) {
emit q->logOutput(tr("Removed \"%1\"").arg(qtVersion->displayName()));
QtVersionManager::removeVersion(qtVersion);
}
};
emit q->logOutput('\n' + tr("Removing toolchain entries..."));
const Toolchains toolchains = ToolChainManager::toolchains();
for (ToolChain *toolChain : toolchains) {
if (toolChain && toolChain->detectionSource() == m_sharedId) {
emit q->logOutput(tr("Removed \"%1\"").arg(toolChain->displayName()));
ToolChainManager::deregisterToolChain(toolChain);
}
};
if (QObject *cmakeManager = ExtensionSystem::PluginManager::getObjectByName("CMakeToolManager")) {
QString logMessage;
const bool res = QMetaObject::invokeMethod(cmakeManager,
"removeDetectedCMake",
Q_ARG(QString, m_sharedId),
Q_ARG(QString *, &logMessage));
QTC_CHECK(res);
emit q->logOutput('\n' + logMessage);
}
if (QObject *debuggerPlugin = ExtensionSystem::PluginManager::getObjectByName("DebuggerPlugin")) {
QString logMessage;
const bool res = QMetaObject::invokeMethod(debuggerPlugin,
"removeDetectedDebuggers",
Q_ARG(QString, m_sharedId),
Q_ARG(QString *, &logMessage));
QTC_CHECK(res);
emit q->logOutput('\n' + logMessage);
}
emit q->logOutput('\n' + tr("Removal of previously auto-detected kit items finished.") + "\n\n");
}
void KitDetectorPrivate::listAutoDetected() const
{
emit q->logOutput(tr("Start listing auto-detected items associated with this docker image."));
emit q->logOutput('\n' + tr("Kits:"));
for (Kit *kit : KitManager::kits()) {
if (kit->autoDetectionSource() == m_sharedId)
emit q->logOutput(kit->displayName());
};
emit q->logOutput('\n' + tr("Qt versions:"));
for (QtVersion *qtVersion : QtVersionManager::versions()) {
if (qtVersion->detectionSource() == m_sharedId)
emit q->logOutput(qtVersion->displayName());
};
emit q->logOutput('\n' + tr("Toolchains:"));
for (ToolChain *toolChain : ToolChainManager::toolchains()) {
if (toolChain->detectionSource() == m_sharedId)
emit q->logOutput(toolChain->displayName());
};
if (QObject *cmakeManager = ExtensionSystem::PluginManager::getObjectByName("CMakeToolManager")) {
QString logMessage;
const bool res = QMetaObject::invokeMethod(cmakeManager,
"listDetectedCMake",
Q_ARG(QString, m_sharedId),
Q_ARG(QString *, &logMessage));
QTC_CHECK(res);
emit q->logOutput('\n' + logMessage);
}
if (QObject *debuggerPlugin = ExtensionSystem::PluginManager::getObjectByName("DebuggerPlugin")) {
QString logMessage;
const bool res = QMetaObject::invokeMethod(debuggerPlugin,
"listDetectedDebuggers",
Q_ARG(QString, m_sharedId),
Q_ARG(QString *, &logMessage));
QTC_CHECK(res);
emit q->logOutput('\n' + logMessage);
}
emit q->logOutput('\n' + tr("Listing of previously auto-detected kit items finished.") + "\n\n");
}
QtVersions KitDetectorPrivate::autoDetectQtVersions() const
{
QtVersions qtVersions;
QString error;
const auto handleQmake = [this, &qtVersions, &error](const FilePath &qmake) {
if (QtVersion *qtVersion = QtVersionFactory::createQtVersionFromQMakePath(qmake, false, m_sharedId, &error)) {
qtVersions.append(qtVersion);
QtVersionManager::addVersion(qtVersion);
emit q->logOutput(tr("Found \"%1\"").arg(qtVersion->qmakeFilePath().toUserOutput()));
}
return true;
};
emit q->logOutput(tr("Searching for qmake executables..."));
const QStringList candidates = {"qmake-qt6", "qmake-qt5", "qmake"};
for (const FilePath &searchPath : m_searchPaths) {
searchPath.iterateDirectory(handleQmake, {candidates, QDir::Files | QDir::Executable,
QDirIterator::Subdirectories});
}
if (!error.isEmpty())
emit q->logOutput(tr("Error: %1.").arg(error));
if (qtVersions.isEmpty())
emit q->logOutput(tr("No Qt installation found."));
return qtVersions;
}
Toolchains KitDetectorPrivate::autoDetectToolChains()
{
const QList<ToolChainFactory *> factories = ToolChainFactory::allToolChainFactories();
Toolchains alreadyKnown = ToolChainManager::toolchains();
Toolchains allNewToolChains;
QApplication::processEvents();
emit q->logOutput('\n' + tr("Searching toolchains..."));
for (ToolChainFactory *factory : factories) {
emit q->logOutput(tr("Searching toolchains of type %1").arg(factory->displayName()));
const ToolchainDetector detector(alreadyKnown, m_device, m_searchPaths);
const Toolchains newToolChains = factory->autoDetect(detector);
for (ToolChain *toolChain : newToolChains) {
emit q->logOutput(tr("Found \"%1\"").arg(toolChain->compilerCommand().toUserOutput()));
toolChain->setDetectionSource(m_sharedId);
ToolChainManager::registerToolChain(toolChain);
alreadyKnown.append(toolChain);
}
allNewToolChains.append(newToolChains);
}
emit q->logOutput(tr("%1 new toolchains found.").arg(allNewToolChains.size()));
return allNewToolChains;
}
void KitDetectorPrivate::autoDetectCMake()
{
QObject *cmakeManager = ExtensionSystem::PluginManager::getObjectByName("CMakeToolManager");
if (!cmakeManager)
return;
QString logMessage;
const bool res = QMetaObject::invokeMethod(cmakeManager,
"autoDetectCMakeForDevice",
Q_ARG(Utils::FilePaths, m_searchPaths),
Q_ARG(QString, m_sharedId),
Q_ARG(QString *, &logMessage));
QTC_CHECK(res);
emit q->logOutput('\n' + logMessage);
}
void KitDetectorPrivate::autoDetectDebugger()
{
QObject *debuggerPlugin = ExtensionSystem::PluginManager::getObjectByName("DebuggerPlugin");
if (!debuggerPlugin)
return;
QString logMessage;
const bool res = QMetaObject::invokeMethod(debuggerPlugin,
"autoDetectDebuggersForDevice",
Q_ARG(Utils::FilePaths, m_searchPaths),
Q_ARG(QString, m_sharedId),
Q_ARG(QString *, &logMessage));
QTC_CHECK(res);
emit q->logOutput('\n' + logMessage);
}
void KitDetectorPrivate::autoDetect()
{
QApplication::setOverrideCursor(Qt::WaitCursor);
undoAutoDetect();
emit q->logOutput(tr("Starting auto-detection. This will take a while..."));
const Toolchains toolchains = autoDetectToolChains();
const QtVersions qtVersions = autoDetectQtVersions();
autoDetectCMake();
autoDetectDebugger();
const auto initializeKit = [this, toolchains, qtVersions](Kit *k) {
k->setAutoDetected(false);
k->setAutoDetectionSource(m_sharedId);
k->setUnexpandedDisplayName("%{Device:Name}");
DeviceTypeKitAspect::setDeviceTypeId(k, Constants::DOCKER_DEVICE_TYPE);
DeviceKitAspect::setDevice(k, m_device);
QtVersion *qt = nullptr;
if (!qtVersions.isEmpty()) {
qt = qtVersions.at(0);
QtSupport::QtKitAspect::setQtVersion(k, qt);
}
Toolchains toolchainsToSet;
toolchainsToSet = ToolChainManager::toolchains([qt, this](const ToolChain *tc){
return tc->detectionSource() == m_sharedId
&& (!qt || qt->qtAbis().contains(tc->targetAbi()));
});
for (ToolChain *toolChain : toolchainsToSet)
ToolChainKitAspect::setToolChain(k, toolChain);
k->setSticky(ToolChainKitAspect::id(), true);
k->setSticky(QtSupport::QtKitAspect::id(), true);
k->setSticky(DeviceKitAspect::id(), true);
k->setSticky(DeviceTypeKitAspect::id(), true);
};
Kit *kit = KitManager::registerKit(initializeKit);
emit q->logOutput('\n' + tr("Registered kit %1").arg(kit->displayName()));
QApplication::restoreOverrideCursor();
}
void DockerDevice::updateContainerAccess() const
{
@@ -1662,21 +1195,6 @@ public:
QString m_selectedId;
};
void DockerDeviceWidget::updateDaemonStateTexts()
{
Utils::optional<bool> daemonState = DockerPlugin::isDaemonRunning();
if (!daemonState.has_value()) {
m_daemonReset->setIcon(Icons::INFO.icon());
m_daemonState->setText(tr("Daemon state not evaluated."));
} else if (daemonState.value()) {
m_daemonReset->setIcon(Icons::OK.icon());
m_daemonState->setText(tr("Docker daemon running."));
} else {
m_daemonReset->setIcon(Icons::CRITICAL.icon());
m_daemonState->setText(tr("Docker daemon not running."));
}
}
// Factory
DockerDeviceFactory::DockerDeviceFactory()

View File

@@ -129,25 +129,6 @@ 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 Utils::FilePaths &selectedPaths) const;
void undoAutoDetect(const QString &sharedId) const;
void listAutoDetected(const QString &sharedId) const;
signals:
void logOutput(const QString &msg);
private:
class KitDetectorPrivate *d = nullptr;
};
class DockerDeviceFactory final : public ProjectExplorer::IDeviceFactory
{
public:

View File

@@ -0,0 +1,225 @@
/****************************************************************************
**
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "dockerdevicewidget.h"
#include <utils/utilsicons.h>
#include <utils/hostosinfo.h>
#include <utils/algorithm.h>
#include <utils/layoutbuilder.h>
#include <QCoreApplication>
#include <QToolButton>
#include <QTextBrowser>
#include <QPushButton>
#include <QComboBox>
using namespace ProjectExplorer;
using namespace Utils;
namespace Docker {
namespace Internal {
DockerDeviceWidget::DockerDeviceWidget(const IDevice::Ptr &device)
: IDeviceWidget(device), m_kitItemDetector(device)
{
auto dockerDevice = device.dynamicCast<DockerDevice>();
QTC_ASSERT(dockerDevice, return);
DockerDeviceData &data = dockerDevice->data();
auto repoLabel = new QLabel(tr("Repository:"));
m_repoLineEdit = new QLineEdit;
m_repoLineEdit->setText(data.repo);
m_repoLineEdit->setEnabled(false);
auto tagLabel = new QLabel(tr("Tag:"));
m_tagLineEdit = new QLineEdit;
m_tagLineEdit->setText(data.tag);
m_tagLineEdit->setEnabled(false);
auto idLabel = new QLabel(tr("Image ID:"));
m_idLineEdit = new QLineEdit;
m_idLineEdit->setText(data.imageId);
m_idLineEdit->setEnabled(false);
auto daemonStateLabel = new QLabel(tr("Daemon state:"));
m_daemonReset = new QToolButton;
m_daemonReset->setToolTip(tr("Clears detected daemon state. "
"It will be automatically re-evaluated next time access is needed."));
m_daemonState = new QLabel;
updateDaemonStateTexts();
connect(m_daemonReset, &QToolButton::clicked, this, [this, dockerDevice] {
DockerPlugin::setGlobalDaemonState(Utils::nullopt);
updateDaemonStateTexts();
});
m_runAsOutsideUser = new QCheckBox(tr("Run as outside user"));
m_runAsOutsideUser->setToolTip(tr("Uses user ID and group ID of the user running Qt Creator "
"in the docker container."));
m_runAsOutsideUser->setChecked(data.useLocalUidGid);
m_runAsOutsideUser->setEnabled(HostOsInfo::isLinuxHost());
connect(m_runAsOutsideUser, &QCheckBox::toggled, this, [&data](bool on) {
data.useLocalUidGid = on;
});
m_pathsListLabel = new InfoLabel(tr("Paths to mount:"));
// FIXME: 8.0: use
//m_pathsListLabel->setToolTip(tr("Source directory list should not be empty"));
m_pathsListEdit = new PathListEditor;
// FIXME: 8.0: use
//m_pathsListEdit->setPlaceholderText(tr("Host directories to mount into the container"));
m_pathsListEdit->setToolTip(tr("Maps paths in this list one-to-one to the "
"docker container."));
m_pathsListEdit->setPathList(data.mounts);
auto markupMounts = [this] {
const bool isEmpty = m_pathsListEdit->pathList().isEmpty();
m_pathsListLabel->setType(isEmpty ? InfoLabel::Warning : InfoLabel::None);
};
markupMounts();
connect(m_pathsListEdit, &PathListEditor::changed, this, [dockerDevice, markupMounts, this] {
dockerDevice->setMounts(m_pathsListEdit->pathList());
markupMounts();
});
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"));
auto listAutoDetectedButton = new QPushButton(tr("List Auto-Detected Kit Items"));
auto searchDirsComboBox = new QComboBox;
searchDirsComboBox->addItem(tr("Search in PATH"));
searchDirsComboBox->addItem(tr("Search in Selected Directories"));
auto searchDirsLineEdit = new FancyLineEdit;
// FIXME: 8.0: use
//searchDirsLineEdit->setPlaceholderText(tr("Semicolon-separated list of directories"));
searchDirsLineEdit->setToolTip(
tr("Select the paths in the docker image that should be scanned for kit entries."));
searchDirsLineEdit->setHistoryCompleter("DockerMounts", true);
auto searchPaths = [searchDirsComboBox, searchDirsLineEdit, dockerDevice] {
FilePaths paths;
if (searchDirsComboBox->currentIndex() == 0) {
paths = dockerDevice->systemEnvironment().path();
} else {
for (const QString &path : searchDirsLineEdit->text().split(';'))
paths.append(FilePath::fromString(path.trimmed()));
}
paths = Utils::transform(paths, [dockerDevice](const FilePath &path) {
return dockerDevice->mapToGlobalPath(path);
});
return paths;
};
connect(autoDetectButton, &QPushButton::clicked, this,
[this, logView, dockerDevice, searchPaths] {
logView->clear();
dockerDevice->updateContainerAccess();
m_kitItemDetector.autoDetect(dockerDevice->id().toString(), searchPaths());
if (DockerPlugin::isDaemonRunning().value_or(false) == false)
logView->append(tr("Docker daemon appears to be not running."));
else
logView->append(tr("Docker daemon appears to be running."));
updateDaemonStateTexts();
});
connect(undoAutoDetectButton, &QPushButton::clicked, this, [this, logView, device] {
logView->clear();
m_kitItemDetector.undoAutoDetect(device->id().toString());
});
connect(listAutoDetectedButton, &QPushButton::clicked, this, [this, logView, device] {
logView->clear();
m_kitItemDetector.listAutoDetected(device->id().toString());
});
using namespace Layouting;
Form {
repoLabel, m_repoLineEdit, Break(),
tagLabel, m_tagLineEdit, Break(),
idLabel, m_idLineEdit, Break(),
daemonStateLabel, m_daemonReset, m_daemonState, Break(),
m_runAsOutsideUser, Break(),
Column {
m_pathsListLabel,
m_pathsListEdit,
}, Break(),
Column {
Space(20),
Row {
searchDirsComboBox,
searchDirsLineEdit
},
Row {
autoDetectButton,
undoAutoDetectButton,
listAutoDetectedButton,
Stretch(),
},
new QLabel(tr("Detection log:")),
logView
}
}.attachTo(this);
searchDirsLineEdit->setVisible(false);
auto updateDirectoriesLineEdit = [searchDirsLineEdit](int index) {
searchDirsLineEdit->setVisible(index == 1);
if (index == 1)
searchDirsLineEdit->setFocus();
};
QObject::connect(searchDirsComboBox, qOverload<int>(&QComboBox::activated),
this, updateDirectoriesLineEdit);
}
void DockerDeviceWidget::updateDaemonStateTexts()
{
Utils::optional<bool> daemonState = DockerPlugin::isDaemonRunning();
if (!daemonState.has_value()) {
m_daemonReset->setIcon(Icons::INFO.icon());
m_daemonState->setText(tr("Daemon state not evaluated."));
} else if (daemonState.value()) {
m_daemonReset->setIcon(Icons::OK.icon());
m_daemonState->setText(tr("Docker daemon running."));
} else {
m_daemonReset->setIcon(Icons::CRITICAL.icon());
m_daemonState->setText(tr("Docker daemon not running."));
}
}
} // Internal
} // Docker

View File

@@ -0,0 +1,65 @@
/****************************************************************************
**
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "dockerplugin.h"
#include "dockerdevice.h"
#include "kitdetector.h"
#include <projectexplorer/devicesupport/idevicewidget.h>
#include <utils/pathlisteditor.h>
#include <QCheckBox>
namespace Docker {
namespace Internal {
class DockerDeviceWidget final : public ProjectExplorer::IDeviceWidget
{
Q_DECLARE_TR_FUNCTIONS(Docker::Internal::DockerDevice)
public:
explicit DockerDeviceWidget(const ProjectExplorer::IDevice::Ptr &device);
void updateDeviceFromUi() final {}
void updateDaemonStateTexts();
private:
QLineEdit *m_repoLineEdit;
QLineEdit *m_tagLineEdit;
QLineEdit *m_idLineEdit;
QToolButton *m_daemonReset;
QLabel *m_daemonState;
QCheckBox *m_runAsOutsideUser;
Utils::InfoLabel *m_pathsListLabel;
Utils::PathListEditor *m_pathsListEdit;
KitDetector m_kitItemDetector;
};
} // Internal
} // Docker

View File

@@ -0,0 +1,339 @@
/****************************************************************************
**
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "kitdetector.h"
#include "dockerconstants.h"
#include <extensionsystem/pluginmanager.h>
#include <projectexplorer/toolchain.h>
#include <projectexplorer/toolchainmanager.h>
#include <qtsupport/baseqtversion.h>
#include <qtsupport/qtkitinformation.h>
#include <qtsupport/qtversionfactory.h>
#include <qtsupport/qtversionmanager.h>
#include <utils/filepath.h>
#include <utils/qtcassert.h>
#include <QApplication>
using namespace ProjectExplorer;
using namespace QtSupport;
using namespace Utils;
namespace Docker {
namespace Internal {
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;
void listAutoDetected() const;
void setSharedId(const QString &sharedId) { m_sharedId = sharedId; }
void setSearchPaths(const FilePaths &searchPaths) { m_searchPaths = searchPaths; }
private:
QtVersions autoDetectQtVersions() const;
QList<ToolChain *> autoDetectToolChains();
void autoDetectCMake();
void autoDetectDebugger();
KitDetector *q;
IDevice::ConstPtr m_device;
QString m_sharedId;
FilePaths m_searchPaths;
};
KitDetector::KitDetector(const IDevice::ConstPtr &device)
: d(new KitDetectorPrivate(this, device))
{}
KitDetector::~KitDetector()
{
delete d;
}
void KitDetector::autoDetect(const QString &sharedId, const FilePaths &searchPaths) const
{
d->setSharedId(sharedId);
d->setSearchPaths(searchPaths);
d->autoDetect();
}
void KitDetector::undoAutoDetect(const QString &sharedId) const
{
d->setSharedId(sharedId);
d->undoAutoDetect();
}
void KitDetector::listAutoDetected(const QString &sharedId) const
{
d->setSharedId(sharedId);
d->listAutoDetected();
}
void KitDetectorPrivate::undoAutoDetect() const
{
emit q->logOutput(tr("Start removing auto-detected items associated with this docker image."));
emit q->logOutput('\n' + tr("Removing kits..."));
for (Kit *kit : KitManager::kits()) {
if (kit->autoDetectionSource() == m_sharedId) {
emit q->logOutput(tr("Removed \"%1\"").arg(kit->displayName()));
KitManager::deregisterKit(kit);
}
};
emit q->logOutput('\n' + tr("Removing Qt version entries..."));
for (QtVersion *qtVersion : QtVersionManager::versions()) {
if (qtVersion->detectionSource() == m_sharedId) {
emit q->logOutput(tr("Removed \"%1\"").arg(qtVersion->displayName()));
QtVersionManager::removeVersion(qtVersion);
}
};
emit q->logOutput('\n' + tr("Removing toolchain entries..."));
const Toolchains toolchains = ToolChainManager::toolchains();
for (ToolChain *toolChain : toolchains) {
if (toolChain && toolChain->detectionSource() == m_sharedId) {
emit q->logOutput(tr("Removed \"%1\"").arg(toolChain->displayName()));
ToolChainManager::deregisterToolChain(toolChain);
}
};
if (QObject *cmakeManager = ExtensionSystem::PluginManager::getObjectByName("CMakeToolManager")) {
QString logMessage;
const bool res = QMetaObject::invokeMethod(cmakeManager,
"removeDetectedCMake",
Q_ARG(QString, m_sharedId),
Q_ARG(QString *, &logMessage));
QTC_CHECK(res);
emit q->logOutput('\n' + logMessage);
}
if (QObject *debuggerPlugin = ExtensionSystem::PluginManager::getObjectByName("DebuggerPlugin")) {
QString logMessage;
const bool res = QMetaObject::invokeMethod(debuggerPlugin,
"removeDetectedDebuggers",
Q_ARG(QString, m_sharedId),
Q_ARG(QString *, &logMessage));
QTC_CHECK(res);
emit q->logOutput('\n' + logMessage);
}
emit q->logOutput('\n' + tr("Removal of previously auto-detected kit items finished.") + "\n\n");
}
void KitDetectorPrivate::listAutoDetected() const
{
emit q->logOutput(tr("Start listing auto-detected items associated with this docker image."));
emit q->logOutput('\n' + tr("Kits:"));
for (Kit *kit : KitManager::kits()) {
if (kit->autoDetectionSource() == m_sharedId)
emit q->logOutput(kit->displayName());
};
emit q->logOutput('\n' + tr("Qt versions:"));
for (QtVersion *qtVersion : QtVersionManager::versions()) {
if (qtVersion->detectionSource() == m_sharedId)
emit q->logOutput(qtVersion->displayName());
};
emit q->logOutput('\n' + tr("Toolchains:"));
for (ToolChain *toolChain : ToolChainManager::toolchains()) {
if (toolChain->detectionSource() == m_sharedId)
emit q->logOutput(toolChain->displayName());
};
if (QObject *cmakeManager = ExtensionSystem::PluginManager::getObjectByName("CMakeToolManager")) {
QString logMessage;
const bool res = QMetaObject::invokeMethod(cmakeManager,
"listDetectedCMake",
Q_ARG(QString, m_sharedId),
Q_ARG(QString *, &logMessage));
QTC_CHECK(res);
emit q->logOutput('\n' + logMessage);
}
if (QObject *debuggerPlugin = ExtensionSystem::PluginManager::getObjectByName("DebuggerPlugin")) {
QString logMessage;
const bool res = QMetaObject::invokeMethod(debuggerPlugin,
"listDetectedDebuggers",
Q_ARG(QString, m_sharedId),
Q_ARG(QString *, &logMessage));
QTC_CHECK(res);
emit q->logOutput('\n' + logMessage);
}
emit q->logOutput('\n' + tr("Listing of previously auto-detected kit items finished.") + "\n\n");
}
QtVersions KitDetectorPrivate::autoDetectQtVersions() const
{
QtVersions qtVersions;
QString error;
const auto handleQmake = [this, &qtVersions, &error](const FilePath &qmake) {
if (QtVersion *qtVersion = QtVersionFactory::createQtVersionFromQMakePath(qmake, false, m_sharedId, &error)) {
qtVersions.append(qtVersion);
QtVersionManager::addVersion(qtVersion);
emit q->logOutput(tr("Found \"%1\"").arg(qtVersion->qmakeFilePath().toUserOutput()));
}
return true;
};
emit q->logOutput(tr("Searching for qmake executables..."));
const QStringList candidates = {"qmake-qt6", "qmake-qt5", "qmake"};
for (const FilePath &searchPath : m_searchPaths) {
searchPath.iterateDirectory(handleQmake, {candidates, QDir::Files | QDir::Executable,
QDirIterator::Subdirectories});
}
if (!error.isEmpty())
emit q->logOutput(tr("Error: %1.").arg(error));
if (qtVersions.isEmpty())
emit q->logOutput(tr("No Qt installation found."));
return qtVersions;
}
Toolchains KitDetectorPrivate::autoDetectToolChains()
{
const QList<ToolChainFactory *> factories = ToolChainFactory::allToolChainFactories();
Toolchains alreadyKnown = ToolChainManager::toolchains();
Toolchains allNewToolChains;
QApplication::processEvents();
emit q->logOutput('\n' + tr("Searching toolchains..."));
for (ToolChainFactory *factory : factories) {
emit q->logOutput(tr("Searching toolchains of type %1").arg(factory->displayName()));
const ToolchainDetector detector(alreadyKnown, m_device, m_searchPaths);
const Toolchains newToolChains = factory->autoDetect(detector);
for (ToolChain *toolChain : newToolChains) {
emit q->logOutput(tr("Found \"%1\"").arg(toolChain->compilerCommand().toUserOutput()));
toolChain->setDetectionSource(m_sharedId);
ToolChainManager::registerToolChain(toolChain);
alreadyKnown.append(toolChain);
}
allNewToolChains.append(newToolChains);
}
emit q->logOutput(tr("%1 new toolchains found.").arg(allNewToolChains.size()));
return allNewToolChains;
}
void KitDetectorPrivate::autoDetectCMake()
{
QObject *cmakeManager = ExtensionSystem::PluginManager::getObjectByName("CMakeToolManager");
if (!cmakeManager)
return;
QString logMessage;
const bool res = QMetaObject::invokeMethod(cmakeManager,
"autoDetectCMakeForDevice",
Q_ARG(Utils::FilePaths, m_searchPaths),
Q_ARG(QString, m_sharedId),
Q_ARG(QString *, &logMessage));
QTC_CHECK(res);
emit q->logOutput('\n' + logMessage);
}
void KitDetectorPrivate::autoDetectDebugger()
{
QObject *debuggerPlugin = ExtensionSystem::PluginManager::getObjectByName("DebuggerPlugin");
if (!debuggerPlugin)
return;
QString logMessage;
const bool res = QMetaObject::invokeMethod(debuggerPlugin,
"autoDetectDebuggersForDevice",
Q_ARG(Utils::FilePaths, m_searchPaths),
Q_ARG(QString, m_sharedId),
Q_ARG(QString *, &logMessage));
QTC_CHECK(res);
emit q->logOutput('\n' + logMessage);
}
void KitDetectorPrivate::autoDetect()
{
QApplication::setOverrideCursor(Qt::WaitCursor);
undoAutoDetect();
emit q->logOutput(tr("Starting auto-detection. This will take a while..."));
const Toolchains toolchains = autoDetectToolChains();
const QtVersions qtVersions = autoDetectQtVersions();
autoDetectCMake();
autoDetectDebugger();
const auto initializeKit = [this, toolchains, qtVersions](Kit *k) {
k->setAutoDetected(false);
k->setAutoDetectionSource(m_sharedId);
k->setUnexpandedDisplayName("%{Device:Name}");
DeviceTypeKitAspect::setDeviceTypeId(k, Constants::DOCKER_DEVICE_TYPE);
DeviceKitAspect::setDevice(k, m_device);
QtVersion *qt = nullptr;
if (!qtVersions.isEmpty()) {
qt = qtVersions.at(0);
QtSupport::QtKitAspect::setQtVersion(k, qt);
}
Toolchains toolchainsToSet;
toolchainsToSet = ToolChainManager::toolchains([qt, this](const ToolChain *tc){
return tc->detectionSource() == m_sharedId
&& (!qt || qt->qtAbis().contains(tc->targetAbi()));
});
for (ToolChain *toolChain : toolchainsToSet)
ToolChainKitAspect::setToolChain(k, toolChain);
k->setSticky(ToolChainKitAspect::id(), true);
k->setSticky(QtSupport::QtKitAspect::id(), true);
k->setSticky(DeviceKitAspect::id(), true);
k->setSticky(DeviceTypeKitAspect::id(), true);
};
Kit *kit = KitManager::registerKit(initializeKit);
emit q->logOutput('\n' + tr("Registered kit %1").arg(kit->displayName()));
QApplication::restoreOverrideCursor();
}
} // Internal
} // Docker

View File

@@ -0,0 +1,55 @@
/****************************************************************************
**
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include <projectexplorer/devicesupport/idevice.h>
#include <QObject>
namespace Docker {
namespace Internal {
class KitDetector : public QObject
{
Q_OBJECT
public:
explicit KitDetector(const ProjectExplorer::IDevice::ConstPtr &device);
~KitDetector() override;
void autoDetect(const QString &sharedId, const Utils::FilePaths &selectedPaths) const;
void undoAutoDetect(const QString &sharedId) const;
void listAutoDetected(const QString &sharedId) const;
signals:
void logOutput(const QString &msg);
private:
class KitDetectorPrivate *d = nullptr;
};
} // Internal
} // Docker