Docker: Make device creation dialog a bit more verbose

Some more visible logging since toolchain detection takes long.

Change-Id: I565a5b0051f485ddf18c2cb034283287d6185190
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
hjk
2021-05-31 10:10:53 +02:00
parent 0b7e71ddca
commit c9ffce8e50
2 changed files with 161 additions and 96 deletions

View File

@@ -50,6 +50,7 @@
#include <utils/environment.h>
#include <utils/hostosinfo.h>
#include <utils/layoutbuilder.h>
#include <utils/overridecursor.h>
#include <utils/port.h>
#include <utils/qtcassert.h>
#include <utils/qtcprocess.h>
@@ -57,6 +58,7 @@
#include <utils/temporaryfile.h>
#include <utils/treemodel.h>
#include <QApplication>
#include <QDialog>
#include <QDialogButtonBox>
#include <QFileSystemWatcher>
@@ -140,22 +142,6 @@ void DockerDeviceProcess::start(const Runnable &runnable)
dockerDevice->runProcess(m_process);
}
void DockerDevice::aboutToBeRemoved() const
{
for (Kit *kit : KitManager::kits()) {
if (kit->autoDetectionSource() == id().toString())
KitManager::deregisterKit(kit);
};
for (BaseQtVersion *qtVersion : QtVersionManager::versions()) {
if (qtVersion->autodetectionSource() == id().toString())
QtVersionManager::removeVersion(qtVersion);
};
// for (ToolChain *toolChain : ToolChainManager::toolChains()) {
// if (toolChain->autoDetectionSource() == id.toString())
// // FIXME: Implement
// };
}
void DockerDeviceProcess::interrupt()
{
device()->signalOperation()->interruptProcess(m_process.processId());
@@ -240,52 +226,10 @@ class DockerPortsGatheringMethod : public PortsGatheringMethod
}
};
class DockerDeviceWidget final : public IDeviceWidget
{
public:
explicit DockerDeviceWidget(const IDevice::Ptr &device)
: IDeviceWidget(device)
{
auto dockerDevice = device.dynamicCast<DockerDevice>();
QTC_ASSERT(dockerDevice, return);
m_idLabel = new QLabel(tr("Image Id:"));
m_idLineEdit = new QLineEdit;
m_idLineEdit->setText(dockerDevice->data().imageId);
m_idLineEdit->setEnabled(false);
m_repoLabel = new QLabel(tr("Repository:"));
m_repoLineEdit = new QLineEdit;
m_repoLineEdit->setText(dockerDevice->data().repo);
m_repoLineEdit->setEnabled(false);
using namespace Layouting;
Form {
m_idLabel, m_idLineEdit, Break(),
m_repoLabel, m_repoLineEdit, Break(),
}.attachTo(this);
}
void updateDeviceFromUi() final {}
private:
QLabel *m_idLabel;
QLineEdit *m_idLineEdit;
QLabel *m_repoLabel;
QLineEdit *m_repoLineEdit;
};
IDeviceWidget *DockerDevice::createWidget()
{
return new DockerDeviceWidget(sharedFromThis());
}
// DockerDevice
class DockerDevicePrivate : public QObject
{
Q_DECLARE_TR_FUNCTIONS(Docker::Internal::DockerDevice)
public:
DockerDevicePrivate(DockerDevice *parent) : q(parent)
{
@@ -305,10 +249,12 @@ public:
void tryCreateLocalFileAccess();
void setupKit();
BaseQtVersion *autoDetectQtVersion() const;
QList<ToolChain *> autoDetectToolChains();
void autoDetectCMake();
void autoDetect(QTextBrowser *log);
void undoAutoDetect(QTextBrowser *log) const;
BaseQtVersion *autoDetectQtVersion(QTextBrowser *log) const;
QList<ToolChain *> autoDetectToolChains(QTextBrowser *log);
void autoDetectCMake(QTextBrowser *log);
void fetchSystemEnviroment();
@@ -324,6 +270,73 @@ public:
Environment m_cachedEnviroment;
};
class DockerDeviceWidget final : public IDeviceWidget
{
Q_DECLARE_TR_FUNCTIONS(Docker::Internal::DockerDevice)
public:
explicit DockerDeviceWidget(const IDevice::Ptr &device)
: IDeviceWidget(device)
{
auto dockerDevice = device.dynamicCast<DockerDevice>();
QTC_ASSERT(dockerDevice, return);
m_idLabel = new QLabel(tr("Image Id:"));
m_idLineEdit = new QLineEdit;
m_idLineEdit->setText(dockerDevice->data().imageId);
m_idLineEdit->setEnabled(false);
m_repoLabel = new QLabel(tr("Repository:"));
m_repoLineEdit = new QLineEdit;
m_repoLineEdit->setText(dockerDevice->data().repo);
m_repoLineEdit->setEnabled(false);
auto logView = new QTextBrowser;
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] {
logView->clear();
dockerDevice->d->autoDetect(logView);
});
connect(undoAutoDetectButton, &QPushButton::clicked, this, [logView, dockerDevice] {
logView->clear();
dockerDevice->d->undoAutoDetect(logView);
});
using namespace Layouting;
Form {
m_idLabel, m_idLineEdit, Break(),
m_repoLabel, m_repoLineEdit, Break(),
Column {
Space(20),
Row { autoDetectButton, undoAutoDetectButton, Stretch() },
new QLabel(tr("Detection Log:")),
logView
}
}.attachTo(this);
}
void updateDeviceFromUi() final {}
private:
QLabel *m_idLabel;
QLineEdit *m_idLineEdit;
QLabel *m_repoLabel;
QLineEdit *m_repoLineEdit;
};
IDeviceWidget *DockerDevice::createWidget()
{
return new DockerDeviceWidget(sharedFromThis());
}
// DockerDevice
DockerDevice::DockerDevice(const DockerDeviceData &data)
: d(new DockerDevicePrivate(this))
{
@@ -377,49 +390,88 @@ const DockerDeviceData &DockerDevice::data() const
return d->m_data;
}
BaseQtVersion *DockerDevicePrivate::autoDetectQtVersion() const
void DockerDevicePrivate::undoAutoDetect(QTextBrowser *log) 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()));
KitManager::deregisterKit(kit);
}
};
for (BaseQtVersion *qtVersion : QtVersionManager::versions()) {
if (qtVersion->autodetectionSource() == id) {
if (log)
log->append(tr("Removing Qt version: %1").arg(qtVersion->displayName()));
QtVersionManager::removeVersion(qtVersion);
}
};
if (log)
log->append(tr("Toolchains not removed."));
// for (ToolChain *toolChain : ToolChainManager::toolChains()) {
// if (toolChain->autoDetectionSource() == id.toString())
// // FIXME: Implement
// };
if (log)
log->append(tr("Removal of auto-detected kit items finished."));
}
BaseQtVersion *DockerDevicePrivate::autoDetectQtVersion(QTextBrowser *log) const
{
QString error;
const QStringList candidates = {"/usr/local/bin/qmake", "/usr/bin/qmake"};
log->append('\n' + tr("Searching Qt installation..."));
for (const QString &candidate : candidates) {
const FilePath qmake = q->mapToGlobalPath(FilePath::fromString(candidate));
if (auto qtVersion = QtVersionFactory::createQtVersionFromQMakePath(qmake, false, m_data.id(), &error)) {
QtVersionManager::addVersion(qtVersion);
log->append(tr("Found Qt: %1").arg(qtVersion->qmakeCommand().toUserOutput()));
return qtVersion;
}
}
log->append(tr("No Qt installation found."));
return nullptr;
}
QList<ToolChain *> DockerDevicePrivate::autoDetectToolChains()
QList<ToolChain *> DockerDevicePrivate::autoDetectToolChains(QTextBrowser *log)
{
const QList<ToolChainFactory *> factories = ToolChainFactory::allToolChainFactories();
QList<ToolChain *> toolChains;
QApplication::processEvents();
log->append('\n' + tr("Searching toolchains..."));
for (ToolChainFactory *factory : factories) {
const QList<ToolChain *> newToolChains = factory->autoDetect(toolChains, q->sharedFromThis());
log->append(tr("Searching toolchains of type %1").arg(factory->displayName()));
for (ToolChain *toolChain : newToolChains) {
LOG("Found ToolChain: " << toolChain->compilerCommand().toUserOutput());
log->append(tr("Found ToolChain: %1").arg(toolChain->compilerCommand().toUserOutput()));
ToolChainManager::registerToolChain(toolChain);
toolChains.append(toolChain);
}
}
log->append(tr("%1 new toolchains found.").arg(toolChains.size()));
return toolChains;
}
void DockerDevicePrivate::autoDetectCMake()
void DockerDevicePrivate::autoDetectCMake(QTextBrowser *log)
{
QObject *cmakeManager = ExtensionSystem::PluginManager::getObjectByName("CMakeToolManager");
if (!cmakeManager)
return;
log->append('\n' + tr("Searching CMake binary..."));
QString error;
const QStringList candidates = {"/usr/local/bin/cmake", "/usr/bin/cmake"};
for (const QString &candidate : candidates) {
const FilePath cmake = q->mapToGlobalPath(FilePath::fromString(candidate));
QTC_CHECK(q->hasLocalFileAccess());
if (cmake.isExecutableFile()) {
log->append(tr("Found CMake binary: %1").arg(cmake.toUserOutput()));
const bool res = QMetaObject::invokeMethod(cmakeManager,
"registerCMakeByPath",
Q_ARG(Utils::FilePath, cmake));
@@ -428,15 +480,21 @@ void DockerDevicePrivate::autoDetectCMake()
}
}
void DockerDevicePrivate::setupKit()
void DockerDevicePrivate::autoDetect(QTextBrowser *log)
{
QApplication::setOverrideCursor(Qt::WaitCursor);
undoAutoDetect(log);
tryCreateLocalFileAccess();
QList<ToolChain *> toolChains = autoDetectToolChains();
if (log)
log->append(tr("Starting auto-detection. This will take a while..."));
BaseQtVersion *qt = autoDetectQtVersion();
QList<ToolChain *> toolChains = autoDetectToolChains(log);
BaseQtVersion *qt = autoDetectQtVersion(log);
autoDetectCMake();
autoDetectCMake(log);
const auto initializeKit = [this, toolChains, qt](Kit *k) {
k->setAutoDetected(false);
@@ -455,7 +513,10 @@ void DockerDevicePrivate::setupKit()
k->setSticky(DeviceTypeKitAspect::id(), true);
};
KitManager::registerKit(initializeKit);
Kit *kit = KitManager::registerKit(initializeKit);
log->append('\n' + tr("Registered Kit %1").arg(kit->displayName()));
QApplication::restoreOverrideCursor();
}
void DockerDevice::tryCreateLocalFileAccess() const
@@ -799,6 +860,11 @@ Environment DockerDevice::systemEnvironment() const
return d->m_cachedEnviroment;
}
void DockerDevice::aboutToBeRemoved() const
{
d->undoAutoDetect(nullptr);
}
void DockerDevicePrivate::fetchSystemEnviroment()
{
SynchronousProcess proc;
@@ -884,37 +950,35 @@ public:
m_view->setSelectionBehavior(QAbstractItemView::SelectRows);
m_view->setSelectionMode(QAbstractItemView::SingleSelection);
auto output = new QTextBrowser;
output->setEnabled(false);
output->setVisible(false);
m_log = new QTextBrowser;
m_log->setVisible(false);
auto buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel,
Qt::Horizontal);
m_buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
using namespace Layouting;
Column {
m_view,
output,
buttons,
m_log,
m_buttons,
}.attachTo(this);
connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
buttons->button(QDialogButtonBox::Ok)->setEnabled(false);
connect(m_buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(m_buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
m_buttons->button(QDialogButtonBox::Ok)->setEnabled(false);
CommandLine cmd{"docker", {"images", "--format", "{{.ID}}\\t{{.Repository}}\\t{{.Tag}}\\t{{.Size}}"}};
output->append(tr("Running \"%1\"\n").arg(cmd.toUserOutput()));
m_log->append(tr("Running \"%1\"\n").arg(cmd.toUserOutput()));
m_process = new QtcProcess(this);
m_process->setCommand(cmd);
connect(m_process, &QtcProcess::readyReadStandardOutput, [this, output] {
connect(m_process, &QtcProcess::readyReadStandardOutput, [this] {
const QString out = QString::fromUtf8(m_process->readAllStandardOutput().trimmed());
output->append(out);
m_log->append(out);
for (const QString &line : out.split('\n')) {
const QStringList parts = line.trimmed().split('\t');
if (parts.size() != 4) {
output->append(tr("Unexpected result: %1").arg(line) + '\n');
m_log->append(tr("Unexpected result: %1").arg(line) + '\n');
continue;
}
auto item = new DockerImageItem;
@@ -924,19 +988,18 @@ public:
item->size = parts.at(3);
m_model.rootItem()->appendChild(item);
}
output->append(tr("\nDone."));
m_log->append(tr("Done."));
});
connect(m_process, &Utils::QtcProcess::readyReadStandardError, [this, output] {
const QString out = tr("Error: %1").arg(QString::fromUtf8(m_process->readAllStandardError()));
output->append(tr("Error: %1").arg(out));
connect(m_process, &Utils::QtcProcess::readyReadStandardError, this, [this] {
const QString out = tr("Error: %1").arg(m_process->stdErr());
m_log->append(tr("Error: %1").arg(out));
});
connect(m_view->selectionModel(), &QItemSelectionModel::selectionChanged, [this, buttons] {
connect(m_view->selectionModel(), &QItemSelectionModel::selectionChanged, [this] {
const QModelIndexList selectedRows = m_view->selectionModel()->selectedRows();
QTC_ASSERT(selectedRows.size() == 1, return);
buttons->button(QDialogButtonBox::Ok)->setEnabled(selectedRows.size() == 1);
m_buttons->button(QDialogButtonBox::Ok)->setEnabled(selectedRows.size() == 1);
});
m_process->start();
@@ -954,13 +1017,15 @@ public:
device->setType(Constants::DOCKER_DEVICE_TYPE);
device->setMachineType(IDevice::Hardware);
device->d->setupKit();
return device;
}
public:
TreeModel<DockerImageItem> m_model;
TreeView *m_view = nullptr;
QTextBrowser *m_log = nullptr;
QDialogButtonBox *m_buttons;
QtcProcess *m_process = nullptr;
QString m_selectedId;
};

View File

@@ -30,7 +30,6 @@
#include <projectexplorer/devicesupport/sshdeviceprocess.h>
#include <utils/aspects.h>
#include <utils/qtcprocess.h>
namespace Docker {
namespace Internal {
@@ -107,6 +106,7 @@ private:
class DockerDevicePrivate *d = nullptr;
friend class DockerDeviceSetupWizard;
friend class DockerDeviceWidget;
};
class DockerDeviceFactory final : public ProjectExplorer::IDeviceFactory