2021-03-29 09:11:36 +02:00
|
|
|
/****************************************************************************
|
|
|
|
|
**
|
|
|
|
|
** Copyright (C) 2016 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 "dockerdevice.h"
|
|
|
|
|
|
|
|
|
|
#include "dockerconstants.h"
|
2021-08-10 16:50:23 +02:00
|
|
|
#include "dockerplugin.h"
|
2021-03-29 09:11:36 +02:00
|
|
|
|
2021-05-17 13:49:43 +02:00
|
|
|
#include <extensionsystem/pluginmanager.h>
|
|
|
|
|
|
2021-03-29 09:11:36 +02:00
|
|
|
#include <coreplugin/icore.h>
|
|
|
|
|
#include <coreplugin/messagemanager.h>
|
|
|
|
|
|
2021-05-31 13:01:49 +02:00
|
|
|
#include <projectexplorer/devicesupport/devicemanager.h>
|
2021-03-29 09:11:36 +02:00
|
|
|
#include <projectexplorer/devicesupport/idevicewidget.h>
|
2021-05-18 06:16:25 +02:00
|
|
|
#include <projectexplorer/kitinformation.h>
|
|
|
|
|
#include <projectexplorer/kitmanager.h>
|
2021-03-29 09:11:36 +02:00
|
|
|
#include <projectexplorer/runcontrol.h>
|
2021-04-29 07:36:34 +02:00
|
|
|
#include <projectexplorer/toolchain.h>
|
|
|
|
|
#include <projectexplorer/toolchainmanager.h>
|
|
|
|
|
|
|
|
|
|
#include <qtsupport/baseqtversion.h>
|
2021-05-18 06:16:25 +02:00
|
|
|
#include <qtsupport/qtkitinformation.h>
|
2021-04-29 07:36:34 +02:00
|
|
|
#include <qtsupport/qtversionfactory.h>
|
|
|
|
|
#include <qtsupport/qtversionmanager.h>
|
2021-03-29 09:11:36 +02:00
|
|
|
|
|
|
|
|
#include <utils/algorithm.h>
|
|
|
|
|
#include <utils/basetreeview.h>
|
|
|
|
|
#include <utils/environment.h>
|
|
|
|
|
#include <utils/hostosinfo.h>
|
2021-07-15 13:56:06 +02:00
|
|
|
#include <utils/utilsicons.h>
|
2021-03-29 09:11:36 +02:00
|
|
|
#include <utils/layoutbuilder.h>
|
2021-05-31 10:10:53 +02:00
|
|
|
#include <utils/overridecursor.h>
|
2021-03-29 09:11:36 +02:00
|
|
|
#include <utils/port.h>
|
|
|
|
|
#include <utils/qtcassert.h>
|
|
|
|
|
#include <utils/qtcprocess.h>
|
|
|
|
|
#include <utils/stringutils.h>
|
2021-04-29 07:36:34 +02:00
|
|
|
#include <utils/temporaryfile.h>
|
2021-03-29 09:11:36 +02:00
|
|
|
#include <utils/treemodel.h>
|
2021-07-07 17:33:32 +02:00
|
|
|
#include <utils/fileutils.h>
|
2021-03-29 09:11:36 +02:00
|
|
|
|
2021-05-31 10:10:53 +02:00
|
|
|
#include <QApplication>
|
2021-06-29 10:47:48 +02:00
|
|
|
#include <QCheckBox>
|
2021-06-14 08:33:44 +02:00
|
|
|
#include <QDateTime>
|
2021-03-29 09:11:36 +02:00
|
|
|
#include <QDialog>
|
|
|
|
|
#include <QDialogButtonBox>
|
2021-04-29 07:36:34 +02:00
|
|
|
#include <QFileSystemWatcher>
|
2021-03-29 09:11:36 +02:00
|
|
|
#include <QHeaderView>
|
2021-09-22 10:31:06 +02:00
|
|
|
#include <QHostAddress>
|
2021-06-28 11:53:29 +02:00
|
|
|
#include <QLoggingCategory>
|
2021-09-22 10:31:06 +02:00
|
|
|
#include <QNetworkInterface>
|
2021-03-29 09:11:36 +02:00
|
|
|
#include <QPushButton>
|
2021-07-20 15:23:26 +02:00
|
|
|
#include <QRandomGenerator>
|
2021-07-22 14:08:53 +02:00
|
|
|
#include <QRegularExpression>
|
2021-03-29 09:11:36 +02:00
|
|
|
#include <QTextBrowser>
|
2021-07-15 13:56:06 +02:00
|
|
|
#include <QToolButton>
|
2021-04-29 07:36:34 +02:00
|
|
|
#include <QThread>
|
2021-03-29 09:11:36 +02:00
|
|
|
|
2021-07-15 10:51:39 +02:00
|
|
|
#include <numeric>
|
|
|
|
|
|
2021-06-28 09:58:54 +02:00
|
|
|
#ifdef Q_OS_UNIX
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
2021-03-29 09:11:36 +02:00
|
|
|
using namespace Core;
|
|
|
|
|
using namespace ProjectExplorer;
|
2021-04-29 07:36:34 +02:00
|
|
|
using namespace QtSupport;
|
2021-03-29 09:11:36 +02:00
|
|
|
using namespace Utils;
|
|
|
|
|
|
|
|
|
|
namespace Docker {
|
|
|
|
|
namespace Internal {
|
|
|
|
|
|
2021-06-28 11:53:29 +02:00
|
|
|
static Q_LOGGING_CATEGORY(dockerDeviceLog, "qtc.docker.device", QtWarningMsg);
|
2021-09-22 14:42:21 +02:00
|
|
|
#define LOG(x) qCDebug(dockerDeviceLog) << this << x << '\n'
|
2021-03-29 09:11:36 +02:00
|
|
|
|
|
|
|
|
class DockerDeviceProcess : public ProjectExplorer::DeviceProcess
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
DockerDeviceProcess(const QSharedPointer<const IDevice> &device, QObject *parent = nullptr);
|
|
|
|
|
~DockerDeviceProcess() {}
|
|
|
|
|
|
|
|
|
|
void start(const Runnable &runnable) override;
|
|
|
|
|
|
|
|
|
|
void interrupt() override;
|
|
|
|
|
void terminate() override { m_process.terminate(); }
|
|
|
|
|
void kill() override;
|
|
|
|
|
|
|
|
|
|
QProcess::ProcessState state() const override;
|
|
|
|
|
QProcess::ExitStatus exitStatus() const override;
|
|
|
|
|
int exitCode() const override;
|
|
|
|
|
QString errorString() const override;
|
|
|
|
|
|
|
|
|
|
QByteArray readAllStandardOutput() override;
|
|
|
|
|
QByteArray readAllStandardError() override;
|
|
|
|
|
|
|
|
|
|
qint64 write(const QByteArray &data) override { return m_process.write(data); }
|
|
|
|
|
|
|
|
|
|
private:
|
2021-04-29 07:36:34 +02:00
|
|
|
QtcProcess m_process;
|
2021-03-29 09:11:36 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
DockerDeviceProcess::DockerDeviceProcess(const QSharedPointer<const IDevice> &device,
|
|
|
|
|
QObject *parent)
|
|
|
|
|
: DeviceProcess(device, parent)
|
2021-08-07 11:41:23 +02:00
|
|
|
, m_process(ProcessMode::Writer)
|
2021-03-29 09:11:36 +02:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DockerDeviceProcess::start(const Runnable &runnable)
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(m_process.state() == QProcess::NotRunning, return);
|
|
|
|
|
DockerDevice::ConstPtr dockerDevice = qSharedPointerCast<const DockerDevice>(device());
|
|
|
|
|
QTC_ASSERT(dockerDevice, return);
|
|
|
|
|
|
|
|
|
|
const QStringList dockerRunFlags = runnable.extraData[Constants::DOCKER_RUN_FLAGS].toStringList();
|
|
|
|
|
|
|
|
|
|
connect(this, &DeviceProcess::readyReadStandardOutput, this, [this] {
|
|
|
|
|
MessageManager::writeSilently(QString::fromLocal8Bit(readAllStandardError()));
|
|
|
|
|
});
|
|
|
|
|
connect(this, &DeviceProcess::readyReadStandardError, this, [this] {
|
|
|
|
|
MessageManager::writeDisrupting(QString::fromLocal8Bit(readAllStandardError()));
|
|
|
|
|
});
|
2021-04-29 07:36:34 +02:00
|
|
|
|
|
|
|
|
disconnect(&m_process);
|
|
|
|
|
|
2021-08-10 09:19:30 +02:00
|
|
|
m_process.setCommand(runnable.command);
|
2021-05-04 06:14:13 +02:00
|
|
|
m_process.setEnvironment(runnable.environment);
|
2021-04-29 07:36:34 +02:00
|
|
|
m_process.setWorkingDirectory(runnable.workingDirectory);
|
2021-05-14 15:21:54 +02:00
|
|
|
connect(&m_process, &QtcProcess::errorOccurred, this, &DeviceProcess::error);
|
|
|
|
|
connect(&m_process, &QtcProcess::finished, this, &DeviceProcess::finished);
|
|
|
|
|
connect(&m_process, &QtcProcess::readyReadStandardOutput,
|
2021-04-29 07:36:34 +02:00
|
|
|
this, &DeviceProcess::readyReadStandardOutput);
|
2021-05-14 15:21:54 +02:00
|
|
|
connect(&m_process, &QtcProcess::readyReadStandardError,
|
2021-04-29 07:36:34 +02:00
|
|
|
this, &DeviceProcess::readyReadStandardError);
|
2021-05-14 15:21:54 +02:00
|
|
|
connect(&m_process, &QtcProcess::started, this, &DeviceProcess::started);
|
2021-08-19 05:56:25 +02:00
|
|
|
|
|
|
|
|
LOG("Running process:" << runnable.command.toUserOutput()
|
|
|
|
|
<< "in" << runnable.workingDirectory.toUserOutput());
|
2021-04-29 07:36:34 +02:00
|
|
|
dockerDevice->runProcess(m_process);
|
2021-03-29 09:11:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DockerDeviceProcess::interrupt()
|
|
|
|
|
{
|
|
|
|
|
device()->signalOperation()->interruptProcess(m_process.processId());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DockerDeviceProcess::kill()
|
|
|
|
|
{
|
|
|
|
|
m_process.kill();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QProcess::ProcessState DockerDeviceProcess::state() const
|
|
|
|
|
{
|
|
|
|
|
return m_process.state();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QProcess::ExitStatus DockerDeviceProcess::exitStatus() const
|
|
|
|
|
{
|
|
|
|
|
return m_process.exitStatus();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int DockerDeviceProcess::exitCode() const
|
|
|
|
|
{
|
|
|
|
|
return m_process.exitCode();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString DockerDeviceProcess::errorString() const
|
|
|
|
|
{
|
|
|
|
|
return m_process.errorString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QByteArray DockerDeviceProcess::readAllStandardOutput()
|
|
|
|
|
{
|
|
|
|
|
return m_process.readAllStandardOutput();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QByteArray DockerDeviceProcess::readAllStandardError()
|
|
|
|
|
{
|
|
|
|
|
return m_process.readAllStandardError();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class DockerPortsGatheringMethod : public PortsGatheringMethod
|
|
|
|
|
{
|
|
|
|
|
Runnable runnable(QAbstractSocket::NetworkLayerProtocol protocol) const override
|
|
|
|
|
{
|
|
|
|
|
// We might encounter the situation that protocol is given IPv6
|
|
|
|
|
// but the consumer of the free port information decides to open
|
|
|
|
|
// an IPv4(only) port. As a result the next IPv6 scan will
|
|
|
|
|
// report the port again as open (in IPv6 namespace), while the
|
|
|
|
|
// same port in IPv4 namespace might still be blocked, and
|
|
|
|
|
// re-use of this port fails.
|
|
|
|
|
// GDBserver behaves exactly like this.
|
|
|
|
|
|
|
|
|
|
Q_UNUSED(protocol)
|
|
|
|
|
|
|
|
|
|
// /proc/net/tcp* covers /proc/net/tcp and /proc/net/tcp6
|
|
|
|
|
Runnable runnable;
|
2021-08-10 16:19:02 +02:00
|
|
|
runnable.command.setExecutable("sed");
|
2021-08-10 09:19:30 +02:00
|
|
|
runnable.command.setArguments("-e 's/.*: [[:xdigit:]]*:\\([[:xdigit:]]\\{4\\}\\).*/\\1/g' /proc/net/tcp*");
|
2021-03-29 09:11:36 +02:00
|
|
|
return runnable;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QList<Utils::Port> usedPorts(const QByteArray &output) const override
|
|
|
|
|
{
|
|
|
|
|
QList<Utils::Port> ports;
|
|
|
|
|
QList<QByteArray> portStrings = output.split('\n');
|
|
|
|
|
foreach (const QByteArray &portString, portStrings) {
|
|
|
|
|
if (portString.size() != 4)
|
|
|
|
|
continue;
|
|
|
|
|
bool ok;
|
|
|
|
|
const Utils::Port port(portString.toInt(&ok, 16));
|
|
|
|
|
if (ok) {
|
|
|
|
|
if (!ports.contains(port))
|
|
|
|
|
ports << port;
|
|
|
|
|
} else {
|
|
|
|
|
qWarning("%s: Unexpected string '%s' is not a port.",
|
|
|
|
|
Q_FUNC_INFO, portString.data());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return ports;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2021-07-01 17:59:15 +02:00
|
|
|
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;
|
2021-07-15 12:25:23 +02:00
|
|
|
void listAutoDetected() const;
|
2021-07-01 17:59:15 +02:00
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-15 12:25:23 +02:00
|
|
|
void KitDetector::listAutoDetected(const QString &sharedId) const
|
|
|
|
|
{
|
|
|
|
|
d->m_sharedId = sharedId;
|
|
|
|
|
d->listAutoDetected();
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-31 10:10:53 +02:00
|
|
|
class DockerDevicePrivate : public QObject
|
|
|
|
|
{
|
|
|
|
|
Q_DECLARE_TR_FUNCTIONS(Docker::Internal::DockerDevice)
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
DockerDevicePrivate(DockerDevice *parent) : q(parent)
|
|
|
|
|
{
|
2021-09-22 14:42:21 +02:00
|
|
|
connect(&m_mergedDirWatcher, &QFileSystemWatcher::fileChanged, this, [this](const QString &path) {
|
2021-05-31 10:10:53 +02:00
|
|
|
Q_UNUSED(path)
|
|
|
|
|
LOG("Container watcher change, file: " << path);
|
|
|
|
|
});
|
2021-09-22 14:42:21 +02:00
|
|
|
connect(&m_mergedDirWatcher, &QFileSystemWatcher::directoryChanged, this, [this](const QString &path) {
|
2021-05-31 10:10:53 +02:00
|
|
|
Q_UNUSED(path)
|
|
|
|
|
LOG("Container watcher change, directory: " << path);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-20 14:27:11 +02:00
|
|
|
~DockerDevicePrivate() { stopCurrentContainer(); }
|
2021-05-31 10:10:53 +02:00
|
|
|
|
2021-07-15 10:10:47 +02:00
|
|
|
bool runInContainer(const CommandLine &cmd) const;
|
2021-07-20 15:23:26 +02:00
|
|
|
bool runInShell(const CommandLine &cmd) const;
|
|
|
|
|
QString outputForRunInShell(const CommandLine &cmd) const;
|
2021-05-31 10:10:53 +02:00
|
|
|
|
|
|
|
|
void tryCreateLocalFileAccess();
|
|
|
|
|
|
2021-07-21 12:38:04 +02:00
|
|
|
void startContainer();
|
2021-07-09 13:18:22 +02:00
|
|
|
void stopCurrentContainer();
|
2021-05-31 10:10:53 +02:00
|
|
|
void fetchSystemEnviroment();
|
|
|
|
|
|
|
|
|
|
DockerDevice *q;
|
|
|
|
|
DockerDeviceData m_data;
|
|
|
|
|
|
|
|
|
|
// For local file access
|
|
|
|
|
QPointer<QtcProcess> m_shell;
|
2021-07-20 15:23:26 +02:00
|
|
|
mutable QMutex m_shellMutex;
|
2021-05-31 10:10:53 +02:00
|
|
|
QString m_container;
|
|
|
|
|
QString m_mergedDir;
|
|
|
|
|
QFileSystemWatcher m_mergedDirWatcher;
|
|
|
|
|
|
|
|
|
|
Environment m_cachedEnviroment;
|
2021-09-14 14:14:15 +02:00
|
|
|
|
|
|
|
|
bool m_useFind = true; // prefer find over ls and hacks, but be able to use ls as fallback
|
2021-05-31 10:10:53 +02:00
|
|
|
};
|
|
|
|
|
|
2021-03-29 09:11:36 +02:00
|
|
|
class DockerDeviceWidget final : public IDeviceWidget
|
|
|
|
|
{
|
2021-05-31 10:10:53 +02:00
|
|
|
Q_DECLARE_TR_FUNCTIONS(Docker::Internal::DockerDevice)
|
|
|
|
|
|
2021-03-29 09:11:36 +02:00
|
|
|
public:
|
|
|
|
|
explicit DockerDeviceWidget(const IDevice::Ptr &device)
|
2021-07-01 17:59:15 +02:00
|
|
|
: IDeviceWidget(device), m_kitItemDetector(device)
|
2021-03-29 09:11:36 +02:00
|
|
|
{
|
|
|
|
|
auto dockerDevice = device.dynamicCast<DockerDevice>();
|
|
|
|
|
QTC_ASSERT(dockerDevice, return);
|
|
|
|
|
|
2021-07-01 17:59:15 +02:00
|
|
|
DockerDeviceData &data = dockerDevice->data();
|
|
|
|
|
|
|
|
|
|
auto idLabel = new QLabel(tr("Image Id:"));
|
2021-03-29 09:11:36 +02:00
|
|
|
m_idLineEdit = new QLineEdit;
|
2021-07-01 17:59:15 +02:00
|
|
|
m_idLineEdit->setText(data.imageId);
|
2021-03-29 09:11:36 +02:00
|
|
|
m_idLineEdit->setEnabled(false);
|
|
|
|
|
|
2021-06-29 12:12:59 +02:00
|
|
|
auto repoLabel = new QLabel(tr("Repository:"));
|
2021-03-29 09:11:36 +02:00
|
|
|
m_repoLineEdit = new QLineEdit;
|
2021-07-01 17:59:15 +02:00
|
|
|
m_repoLineEdit->setText(data.repo);
|
2021-03-29 09:11:36 +02:00
|
|
|
m_repoLineEdit->setEnabled(false);
|
|
|
|
|
|
2021-07-15 13:56:06 +02:00
|
|
|
auto daemonStateLabel = new QLabel(tr("Daemon state:"));
|
|
|
|
|
m_daemonReset = new QToolButton;
|
2021-07-16 13:05:40 +02:00
|
|
|
m_daemonReset->setToolTip(tr("Clear detected daemon state. "
|
|
|
|
|
"It will be automatically re-evaluated next time an access is needed."));
|
|
|
|
|
|
2021-08-10 16:50:23 +02:00
|
|
|
m_daemonState = new QLabel;
|
|
|
|
|
updateDaemonStateTexts();
|
2021-07-15 13:56:06 +02:00
|
|
|
|
|
|
|
|
connect(m_daemonReset, &QToolButton::clicked, this, [this, dockerDevice] {
|
2021-08-10 16:50:23 +02:00
|
|
|
DockerPlugin::setGlobalDaemonState(Utils::nullopt);
|
|
|
|
|
updateDaemonStateTexts();
|
2021-07-15 13:56:06 +02:00
|
|
|
});
|
|
|
|
|
|
2021-06-29 10:47:48 +02:00
|
|
|
m_runAsOutsideUser = new QCheckBox(tr("Run as outside user"));
|
2021-07-02 14:56:41 +02:00
|
|
|
m_runAsOutsideUser->setToolTip(tr("Use user ID and group ID of the user running Qt Creator "
|
|
|
|
|
"in the Docker container."));
|
2021-07-01 17:59:15 +02:00
|
|
|
m_runAsOutsideUser->setChecked(data.useLocalUidGid);
|
2021-06-29 10:47:48 +02:00
|
|
|
m_runAsOutsideUser->setEnabled(HostOsInfo::isLinuxHost());
|
|
|
|
|
|
2021-07-01 17:59:15 +02:00
|
|
|
connect(m_runAsOutsideUser, &QCheckBox::toggled, this, [&data](bool on) {
|
|
|
|
|
data.useLocalUidGid = on;
|
2021-06-29 12:12:59 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
m_pathsLineEdit = new QLineEdit;
|
2021-07-01 17:59:15 +02:00
|
|
|
m_pathsLineEdit->setText(data.repo);
|
2021-06-29 12:12:59 +02:00
|
|
|
m_pathsLineEdit->setToolTip(tr("Paths in this semi-colon separated list will be "
|
2021-07-02 14:56:41 +02:00
|
|
|
"mapped one-to-one into the Docker container."));
|
2021-07-01 17:59:15 +02:00
|
|
|
m_pathsLineEdit->setText(data.mounts.join(';'));
|
2021-07-09 16:10:31 +02:00
|
|
|
m_pathsLineEdit->setPlaceholderText(tr("List project source directories here"));
|
2021-06-29 12:12:59 +02:00
|
|
|
|
2021-07-09 13:18:22 +02:00
|
|
|
connect(m_pathsLineEdit, &QLineEdit::textChanged, this, [dockerDevice](const QString &text) {
|
2021-07-12 15:27:19 +02:00
|
|
|
dockerDevice->setMounts(text.split(';', Qt::SkipEmptyParts));
|
2021-06-29 12:12:59 +02:00
|
|
|
});
|
|
|
|
|
|
2021-05-31 10:10:53 +02:00
|
|
|
auto logView = new QTextBrowser;
|
2021-07-01 17:59:15 +02:00
|
|
|
connect(&m_kitItemDetector, &KitDetector::logOutput,
|
|
|
|
|
logView, &QTextBrowser::append);
|
2021-05-31 10:10:53 +02:00
|
|
|
|
|
|
|
|
auto autoDetectButton = new QPushButton(tr("Auto-detect Kit Items"));
|
|
|
|
|
auto undoAutoDetectButton = new QPushButton(tr("Remove Auto-Detected Kit Items"));
|
2021-07-15 12:25:23 +02:00
|
|
|
auto listAutoDetectedButton = new QPushButton(tr("List Auto-Detected Kit Items"));
|
2021-05-31 10:10:53 +02:00
|
|
|
|
2021-07-01 17:59:15 +02:00
|
|
|
connect(autoDetectButton, &QPushButton::clicked, this, [this, logView, id = data.id(), dockerDevice] {
|
2021-05-31 10:10:53 +02:00
|
|
|
logView->clear();
|
2021-07-01 17:59:15 +02:00
|
|
|
dockerDevice->tryCreateLocalFileAccess();
|
|
|
|
|
m_kitItemDetector.autoDetect(id);
|
2021-07-15 13:56:06 +02:00
|
|
|
|
2021-08-10 16:50:23 +02:00
|
|
|
if (DockerPlugin::isDaemonRunning().value_or(false) == false)
|
2021-07-15 13:56:06 +02:00
|
|
|
logView->append(tr("Docker daemon appears to be not running."));
|
2021-08-10 16:50:23 +02:00
|
|
|
else
|
2021-07-16 13:05:40 +02:00
|
|
|
logView->append(tr("Docker daemon appears to be running."));
|
2021-08-10 16:50:23 +02:00
|
|
|
updateDaemonStateTexts();
|
2021-05-31 10:10:53 +02:00
|
|
|
});
|
|
|
|
|
|
2021-07-01 17:59:15 +02:00
|
|
|
connect(undoAutoDetectButton, &QPushButton::clicked, this, [this, logView, id = data.id()] {
|
2021-05-31 10:10:53 +02:00
|
|
|
logView->clear();
|
2021-07-01 17:59:15 +02:00
|
|
|
m_kitItemDetector.undoAutoDetect(id);
|
2021-05-31 10:10:53 +02:00
|
|
|
});
|
|
|
|
|
|
2021-07-15 12:25:23 +02:00
|
|
|
connect(listAutoDetectedButton, &QPushButton::clicked, this, [this, logView, id = data.id()] {
|
|
|
|
|
logView->clear();
|
|
|
|
|
m_kitItemDetector.listAutoDetected(id);
|
|
|
|
|
});
|
|
|
|
|
|
2021-03-29 09:11:36 +02:00
|
|
|
using namespace Layouting;
|
|
|
|
|
|
|
|
|
|
Form {
|
2021-06-29 12:12:59 +02:00
|
|
|
idLabel, m_idLineEdit, Break(),
|
|
|
|
|
repoLabel, m_repoLineEdit, Break(),
|
2021-07-16 13:05:40 +02:00
|
|
|
daemonStateLabel, m_daemonReset, m_daemonState, Break(),
|
2021-06-29 10:47:48 +02:00
|
|
|
m_runAsOutsideUser, Break(),
|
2021-06-29 12:12:59 +02:00
|
|
|
tr("Paths to mount:"), m_pathsLineEdit, Break(),
|
2021-05-31 10:10:53 +02:00
|
|
|
Column {
|
|
|
|
|
Space(20),
|
2021-07-15 12:25:23 +02:00
|
|
|
Row { autoDetectButton, undoAutoDetectButton, listAutoDetectedButton, Stretch() },
|
2021-07-02 14:56:41 +02:00
|
|
|
new QLabel(tr("Detection log:")),
|
2021-05-31 10:10:53 +02:00
|
|
|
logView
|
|
|
|
|
}
|
2021-03-29 09:11:36 +02:00
|
|
|
}.attachTo(this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void updateDeviceFromUi() final {}
|
2021-08-10 16:50:23 +02:00
|
|
|
void updateDaemonStateTexts();
|
2021-03-29 09:11:36 +02:00
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
QLineEdit *m_idLineEdit;
|
|
|
|
|
QLineEdit *m_repoLineEdit;
|
2021-07-15 13:56:06 +02:00
|
|
|
QToolButton *m_daemonReset;
|
2021-07-16 13:05:40 +02:00
|
|
|
QLabel *m_daemonState;
|
2021-06-29 10:47:48 +02:00
|
|
|
QCheckBox *m_runAsOutsideUser;
|
2021-06-29 12:12:59 +02:00
|
|
|
QLineEdit *m_pathsLineEdit;
|
2021-07-01 17:59:15 +02:00
|
|
|
|
|
|
|
|
KitDetector m_kitItemDetector;
|
2021-03-29 09:11:36 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
IDeviceWidget *DockerDevice::createWidget()
|
|
|
|
|
{
|
|
|
|
|
return new DockerDeviceWidget(sharedFromThis());
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-29 07:36:34 +02:00
|
|
|
|
|
|
|
|
// DockerDevice
|
|
|
|
|
|
2021-03-29 09:11:36 +02:00
|
|
|
DockerDevice::DockerDevice(const DockerDeviceData &data)
|
2021-05-18 06:16:25 +02:00
|
|
|
: d(new DockerDevicePrivate(this))
|
2021-03-29 09:11:36 +02:00
|
|
|
{
|
2021-04-29 07:36:34 +02:00
|
|
|
d->m_data = data;
|
|
|
|
|
|
2021-03-29 09:11:36 +02:00
|
|
|
setDisplayType(tr("Docker"));
|
|
|
|
|
setOsType(OsTypeOtherUnix);
|
|
|
|
|
setDefaultDisplayName(tr("Docker Image"));;
|
|
|
|
|
setDisplayName(tr("Docker Image \"%1\" (%2)").arg(data.repo).arg(data.imageId));
|
|
|
|
|
setAllowEmptyCommand(true);
|
|
|
|
|
|
2021-08-02 18:02:10 +02:00
|
|
|
setOpenTerminal([this](const Environment &env, const FilePath &workingDir) {
|
2021-03-29 09:11:36 +02:00
|
|
|
DeviceProcess * const proc = createProcess(nullptr);
|
|
|
|
|
QObject::connect(proc, &DeviceProcess::finished, [proc] {
|
|
|
|
|
if (!proc->errorString().isEmpty()) {
|
|
|
|
|
MessageManager::writeDisrupting(
|
|
|
|
|
tr("Error running remote shell: %1").arg(proc->errorString()));
|
|
|
|
|
}
|
|
|
|
|
proc->deleteLater();
|
|
|
|
|
});
|
|
|
|
|
QObject::connect(proc, &DeviceProcess::error, [proc] {
|
|
|
|
|
MessageManager::writeDisrupting(tr("Error starting remote shell."));
|
|
|
|
|
proc->deleteLater();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
Runnable runnable;
|
2021-08-10 09:19:30 +02:00
|
|
|
runnable.command = {"/bin/sh", {}};
|
2021-03-29 09:11:36 +02:00
|
|
|
runnable.device = sharedFromThis();
|
|
|
|
|
runnable.environment = env;
|
|
|
|
|
runnable.workingDirectory = workingDir;
|
|
|
|
|
runnable.extraData[Constants::DOCKER_RUN_FLAGS] = QStringList({"--interactive", "--tty"});
|
|
|
|
|
|
|
|
|
|
proc->setRunInTerminal(true);
|
|
|
|
|
proc->start(runnable);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (HostOsInfo::isAnyUnixHost()) {
|
|
|
|
|
addDeviceAction({tr("Open Shell in Container"), [](const IDevice::Ptr &device, QWidget *) {
|
2021-08-02 18:02:10 +02:00
|
|
|
device->openTerminal(Environment(), FilePath());
|
2021-03-29 09:11:36 +02:00
|
|
|
}});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-29 07:36:34 +02:00
|
|
|
DockerDevice::~DockerDevice()
|
|
|
|
|
{
|
|
|
|
|
delete d;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-29 09:11:36 +02:00
|
|
|
const DockerDeviceData &DockerDevice::data() const
|
|
|
|
|
{
|
2021-04-29 07:36:34 +02:00
|
|
|
return d->m_data;
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-29 12:12:59 +02:00
|
|
|
DockerDeviceData &DockerDevice::data()
|
|
|
|
|
{
|
|
|
|
|
return d->m_data;
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-01 17:59:15 +02:00
|
|
|
void KitDetectorPrivate::undoAutoDetect() const
|
2021-05-31 10:10:53 +02:00
|
|
|
{
|
2021-07-13 13:05:36 +02:00
|
|
|
emit q->logOutput(tr("Start removing auto-detected items associated with this docker image."));
|
|
|
|
|
|
|
|
|
|
emit q->logOutput('\n' + tr("Removing kits..."));
|
2021-05-31 10:10:53 +02:00
|
|
|
for (Kit *kit : KitManager::kits()) {
|
2021-07-01 17:59:15 +02:00
|
|
|
if (kit->autoDetectionSource() == m_sharedId) {
|
2021-07-13 13:05:36 +02:00
|
|
|
emit q->logOutput(tr("Removed \"%1\"").arg(kit->displayName()));
|
2021-05-31 10:10:53 +02:00
|
|
|
KitManager::deregisterKit(kit);
|
|
|
|
|
}
|
|
|
|
|
};
|
2021-07-13 13:05:36 +02:00
|
|
|
|
|
|
|
|
emit q->logOutput('\n' + tr("Removing Qt version entries..."));
|
2021-05-31 10:10:53 +02:00
|
|
|
for (BaseQtVersion *qtVersion : QtVersionManager::versions()) {
|
2021-07-12 16:10:43 +02:00
|
|
|
if (qtVersion->detectionSource() == m_sharedId) {
|
2021-07-13 13:05:36 +02:00
|
|
|
emit q->logOutput(tr("Removed \"%1\"").arg(qtVersion->displayName()));
|
2021-05-31 10:10:53 +02:00
|
|
|
QtVersionManager::removeVersion(qtVersion);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2021-07-13 13:05:36 +02:00
|
|
|
emit q->logOutput('\n' + tr("Removing toolchain entries..."));
|
|
|
|
|
for (ToolChain *toolChain : ToolChainManager::toolChains()) {
|
|
|
|
|
QString detectionSource = toolChain->detectionSource();
|
|
|
|
|
if (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);
|
|
|
|
|
}
|
2021-05-31 10:10:53 +02:00
|
|
|
|
2021-07-13 13:05:36 +02:00
|
|
|
emit q->logOutput('\n' + tr("Removal of previously auto-detected kit items finished.") + "\n\n");
|
2021-05-31 10:10:53 +02:00
|
|
|
}
|
|
|
|
|
|
2021-07-15 12:25:23 +02:00
|
|
|
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 (BaseQtVersion *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");
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-01 17:59:15 +02:00
|
|
|
QList<BaseQtVersion *> KitDetectorPrivate::autoDetectQtVersions() const
|
2021-04-29 07:36:34 +02:00
|
|
|
{
|
2021-06-18 17:32:21 +02:00
|
|
|
QList<BaseQtVersion *> qtVersions;
|
|
|
|
|
|
2021-05-31 11:22:51 +02:00
|
|
|
QString error;
|
2021-06-18 17:32:21 +02:00
|
|
|
const QStringList candidates = {"qmake-qt6", "qmake-qt5", "qmake"};
|
2021-07-01 17:59:15 +02:00
|
|
|
emit q->logOutput('\n' + tr("Searching Qt installations..."));
|
2021-05-31 11:22:51 +02:00
|
|
|
for (const QString &candidate : candidates) {
|
2021-07-01 17:59:15 +02:00
|
|
|
emit q->logOutput(tr("Searching for %1 executable...").arg(candidate));
|
2021-07-08 06:53:28 +02:00
|
|
|
const FilePath qmake = m_device->searchExecutableInPath(candidate);
|
2021-06-18 17:32:21 +02:00
|
|
|
if (qmake.isEmpty())
|
|
|
|
|
continue;
|
2021-07-01 17:59:15 +02:00
|
|
|
BaseQtVersion *qtVersion = QtVersionFactory::createQtVersionFromQMakePath(qmake, false, m_sharedId, &error);
|
2021-06-18 17:32:21 +02:00
|
|
|
if (!qtVersion)
|
|
|
|
|
continue;
|
|
|
|
|
qtVersions.append(qtVersion);
|
|
|
|
|
QtVersionManager::addVersion(qtVersion);
|
2021-07-13 13:05:36 +02:00
|
|
|
emit q->logOutput(tr("Found \"%1\"").arg(qtVersion->qmakeFilePath().toUserOutput()));
|
2021-05-31 11:22:51 +02:00
|
|
|
}
|
2021-07-01 17:59:15 +02:00
|
|
|
if (qtVersions.isEmpty())
|
|
|
|
|
emit q->logOutput(tr("No Qt installation found."));
|
2021-06-18 17:32:21 +02:00
|
|
|
return qtVersions;
|
2021-04-29 07:36:34 +02:00
|
|
|
}
|
|
|
|
|
|
2021-07-01 17:59:15 +02:00
|
|
|
QList<ToolChain *> KitDetectorPrivate::autoDetectToolChains()
|
2021-04-29 07:36:34 +02:00
|
|
|
{
|
2021-05-31 11:22:51 +02:00
|
|
|
const QList<ToolChainFactory *> factories = ToolChainFactory::allToolChainFactories();
|
2021-04-29 07:36:34 +02:00
|
|
|
|
2021-07-13 13:05:36 +02:00
|
|
|
QList<ToolChain *> alreadyKnown = ToolChainManager::toolChains();
|
|
|
|
|
QList<ToolChain *> allNewToolChains;
|
2021-05-31 10:10:53 +02:00
|
|
|
QApplication::processEvents();
|
2021-07-13 13:05:36 +02:00
|
|
|
emit q->logOutput('\n' + tr("Searching toolchains..."));
|
2021-05-31 11:22:51 +02:00
|
|
|
for (ToolChainFactory *factory : factories) {
|
2021-07-13 13:05:36 +02:00
|
|
|
emit q->logOutput(tr("Searching toolchains of type %1").arg(factory->displayName()));
|
|
|
|
|
const QList<ToolChain *> newToolChains = factory->autoDetect(alreadyKnown, m_device.constCast<IDevice>());
|
2021-04-29 07:36:34 +02:00
|
|
|
for (ToolChain *toolChain : newToolChains) {
|
2021-07-13 13:05:36 +02:00
|
|
|
emit q->logOutput(tr("Found \"%1\"").arg(toolChain->compilerCommand().toUserOutput()));
|
|
|
|
|
toolChain->setDetectionSource(m_sharedId);
|
2021-04-29 07:36:34 +02:00
|
|
|
ToolChainManager::registerToolChain(toolChain);
|
2021-07-13 13:05:36 +02:00
|
|
|
alreadyKnown.append(toolChain);
|
2021-04-29 07:36:34 +02:00
|
|
|
}
|
2021-07-13 13:05:36 +02:00
|
|
|
allNewToolChains.append(newToolChains);
|
2021-05-31 11:22:51 +02:00
|
|
|
}
|
2021-07-13 13:05:36 +02:00
|
|
|
emit q->logOutput(tr("%1 new toolchains found.").arg(allNewToolChains.size()));
|
2021-05-18 06:16:25 +02:00
|
|
|
|
2021-07-13 13:05:36 +02:00
|
|
|
return allNewToolChains;
|
2021-04-29 07:36:34 +02:00
|
|
|
}
|
|
|
|
|
|
2021-07-01 17:59:15 +02:00
|
|
|
void KitDetectorPrivate::autoDetectCMake()
|
2021-05-17 13:49:43 +02:00
|
|
|
{
|
|
|
|
|
QObject *cmakeManager = ExtensionSystem::PluginManager::getObjectByName("CMakeToolManager");
|
|
|
|
|
if (!cmakeManager)
|
|
|
|
|
return;
|
|
|
|
|
|
2021-07-12 14:23:06 +02:00
|
|
|
const FilePath deviceRoot = m_device->mapToGlobalPath({});
|
2021-07-13 13:05:36 +02:00
|
|
|
QString logMessage;
|
2021-07-12 14:23:06 +02:00
|
|
|
const bool res = QMetaObject::invokeMethod(cmakeManager,
|
|
|
|
|
"autoDetectCMakeForDevice",
|
|
|
|
|
Q_ARG(Utils::FilePath, deviceRoot),
|
|
|
|
|
Q_ARG(QString, m_sharedId),
|
2021-07-13 13:05:36 +02:00
|
|
|
Q_ARG(QString *, &logMessage));
|
2021-07-12 14:23:06 +02:00
|
|
|
QTC_CHECK(res);
|
2021-07-13 13:05:36 +02:00
|
|
|
emit q->logOutput('\n' + logMessage);
|
2021-05-18 06:16:25 +02:00
|
|
|
}
|
|
|
|
|
|
2021-07-01 17:59:15 +02:00
|
|
|
void KitDetectorPrivate::autoDetectDebugger()
|
2021-06-21 10:27:02 +02:00
|
|
|
{
|
|
|
|
|
QObject *debuggerPlugin = ExtensionSystem::PluginManager::getObjectByName("DebuggerPlugin");
|
|
|
|
|
if (!debuggerPlugin)
|
|
|
|
|
return;
|
|
|
|
|
|
2021-07-01 17:59:15 +02:00
|
|
|
const FilePath deviceRoot = m_device->mapToGlobalPath({});
|
2021-07-13 13:05:36 +02:00
|
|
|
QString logMessage;
|
2021-06-21 10:27:02 +02:00
|
|
|
const bool res = QMetaObject::invokeMethod(debuggerPlugin,
|
|
|
|
|
"autoDetectDebuggersForDevice",
|
2021-07-09 17:39:50 +02:00
|
|
|
Q_ARG(Utils::FilePath, deviceRoot),
|
2021-07-13 13:05:36 +02:00
|
|
|
Q_ARG(QString, m_sharedId),
|
|
|
|
|
Q_ARG(QString *, &logMessage));
|
2021-06-21 10:27:02 +02:00
|
|
|
QTC_CHECK(res);
|
2021-07-13 13:05:36 +02:00
|
|
|
emit q->logOutput('\n' + logMessage);
|
2021-06-21 10:27:02 +02:00
|
|
|
}
|
|
|
|
|
|
2021-07-01 17:59:15 +02:00
|
|
|
void KitDetectorPrivate::autoDetect()
|
2021-05-18 06:16:25 +02:00
|
|
|
{
|
2021-05-31 10:10:53 +02:00
|
|
|
QApplication::setOverrideCursor(Qt::WaitCursor);
|
|
|
|
|
|
2021-07-01 17:59:15 +02:00
|
|
|
undoAutoDetect();
|
2021-05-18 06:16:25 +02:00
|
|
|
|
2021-07-01 17:59:15 +02:00
|
|
|
emit q->logOutput(tr("Starting auto-detection. This will take a while..."));
|
2021-05-18 06:16:25 +02:00
|
|
|
|
2021-07-01 17:59:15 +02:00
|
|
|
QList<ToolChain *> toolChains = autoDetectToolChains();
|
|
|
|
|
QList<BaseQtVersion *> qtVersions = autoDetectQtVersions();
|
2021-05-18 06:16:25 +02:00
|
|
|
|
2021-07-01 17:59:15 +02:00
|
|
|
autoDetectCMake();
|
|
|
|
|
autoDetectDebugger();
|
2021-05-18 06:16:25 +02:00
|
|
|
|
2021-06-18 17:32:21 +02:00
|
|
|
const auto initializeKit = [this, toolChains, qtVersions](Kit *k) {
|
2021-05-18 06:16:25 +02:00
|
|
|
k->setAutoDetected(false);
|
2021-07-01 17:59:15 +02:00
|
|
|
k->setAutoDetectionSource(m_sharedId);
|
2021-05-18 06:16:25 +02:00
|
|
|
k->setUnexpandedDisplayName("%{Device:Name}");
|
|
|
|
|
|
|
|
|
|
DeviceTypeKitAspect::setDeviceTypeId(k, Constants::DOCKER_DEVICE_TYPE);
|
2021-07-01 17:59:15 +02:00
|
|
|
DeviceKitAspect::setDevice(k, m_device);
|
2021-05-18 06:16:25 +02:00
|
|
|
for (ToolChain *tc : toolChains)
|
|
|
|
|
ToolChainKitAspect::setToolChain(k, tc);
|
2021-06-18 17:32:21 +02:00
|
|
|
if (!qtVersions.isEmpty())
|
|
|
|
|
QtSupport::QtKitAspect::setQtVersion(k, qtVersions.at(0));
|
2021-05-18 06:16:25 +02:00
|
|
|
|
|
|
|
|
k->setSticky(ToolChainKitAspect::id(), true);
|
|
|
|
|
k->setSticky(QtSupport::QtKitAspect::id(), true);
|
|
|
|
|
k->setSticky(DeviceKitAspect::id(), true);
|
|
|
|
|
k->setSticky(DeviceTypeKitAspect::id(), true);
|
|
|
|
|
};
|
|
|
|
|
|
2021-05-31 10:10:53 +02:00
|
|
|
Kit *kit = KitManager::registerKit(initializeKit);
|
2021-07-01 17:59:15 +02:00
|
|
|
emit q->logOutput('\n' + tr("Registered kit %1").arg(kit->displayName()));
|
2021-05-31 10:10:53 +02:00
|
|
|
|
|
|
|
|
QApplication::restoreOverrideCursor();
|
2021-05-17 13:49:43 +02:00
|
|
|
}
|
|
|
|
|
|
2021-04-29 07:36:34 +02:00
|
|
|
void DockerDevice::tryCreateLocalFileAccess() const
|
|
|
|
|
{
|
2021-05-18 06:16:25 +02:00
|
|
|
d->tryCreateLocalFileAccess();
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-09 13:18:22 +02:00
|
|
|
void DockerDevicePrivate::stopCurrentContainer()
|
|
|
|
|
{
|
2021-08-10 16:50:23 +02:00
|
|
|
if (m_container.isEmpty() || !DockerPlugin::isDaemonRunning().value_or(false))
|
2021-07-09 13:18:22 +02:00
|
|
|
return;
|
|
|
|
|
|
2021-07-20 14:27:11 +02:00
|
|
|
if (m_shell) {
|
2021-07-20 15:23:26 +02:00
|
|
|
QMutexLocker l(&m_shellMutex);
|
2021-07-20 14:27:11 +02:00
|
|
|
m_shell->write("exit\n");
|
|
|
|
|
m_shell->waitForFinished(2000);
|
|
|
|
|
if (m_shell->state() == QProcess::NotRunning) {
|
|
|
|
|
LOG("Clean exit via shell");
|
|
|
|
|
m_container.clear();
|
|
|
|
|
m_mergedDir.clear();
|
|
|
|
|
delete m_shell;
|
|
|
|
|
m_shell = nullptr;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-09 13:18:22 +02:00
|
|
|
QtcProcess proc;
|
|
|
|
|
proc.setCommand({"docker", {"container", "stop", m_container}});
|
|
|
|
|
|
|
|
|
|
m_container.clear();
|
|
|
|
|
m_mergedDir.clear();
|
|
|
|
|
|
|
|
|
|
proc.runBlocking();
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-22 10:31:06 +02:00
|
|
|
static QString getLocalIPv4Address()
|
|
|
|
|
{
|
|
|
|
|
const QList<QHostAddress> addresses = QNetworkInterface::allAddresses();
|
|
|
|
|
for (auto &a : addresses) {
|
|
|
|
|
if (a.isInSubnet(QHostAddress("192.168.0.0"), 16))
|
|
|
|
|
return a.toString();
|
|
|
|
|
if (a.isInSubnet(QHostAddress("10.0.0.0"), 8))
|
|
|
|
|
return a.toString();
|
|
|
|
|
if (a.isInSubnet(QHostAddress("172.16.0.0"), 12))
|
|
|
|
|
return a.toString();
|
|
|
|
|
}
|
|
|
|
|
return QString();
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-21 12:38:04 +02:00
|
|
|
void DockerDevicePrivate::startContainer()
|
2021-05-18 06:16:25 +02:00
|
|
|
{
|
2021-04-29 07:36:34 +02:00
|
|
|
QString tempFileName;
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
TemporaryFile temp("qtc-docker-XXXXXX");
|
|
|
|
|
temp.open();
|
|
|
|
|
tempFileName = temp.fileName();
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-22 10:31:06 +02:00
|
|
|
const QString display = HostOsInfo::isWindowsHost() ? QString(getLocalIPv4Address() + ":0.0")
|
|
|
|
|
: QString(":0");
|
2021-06-17 10:34:46 +02:00
|
|
|
CommandLine dockerRun{"docker", {"run", "-i", "--cidfile=" + tempFileName,
|
2021-06-21 16:50:20 +02:00
|
|
|
"--rm",
|
2021-09-22 10:31:06 +02:00
|
|
|
"-e", QString("DISPLAY=%1").arg(display),
|
2021-05-31 11:22:51 +02:00
|
|
|
"-e", "XAUTHORITY=/.Xauthority",
|
2021-06-17 10:34:46 +02:00
|
|
|
"--net", "host"}};
|
|
|
|
|
|
2021-06-28 09:58:54 +02:00
|
|
|
#ifdef Q_OS_UNIX
|
2021-06-29 10:47:48 +02:00
|
|
|
if (m_data.useLocalUidGid)
|
|
|
|
|
dockerRun.addArgs({"-u", QString("%1:%2").arg(getuid()).arg(getgid())});
|
2021-06-28 09:58:54 +02:00
|
|
|
#endif
|
|
|
|
|
|
2021-07-12 15:27:19 +02:00
|
|
|
for (const QString &mount : qAsConst(m_data.mounts)) {
|
|
|
|
|
if (!mount.isEmpty())
|
|
|
|
|
dockerRun.addArgs({"-v", mount + ':' + mount});
|
|
|
|
|
}
|
2021-06-17 10:34:46 +02:00
|
|
|
|
2021-09-20 14:13:24 +02:00
|
|
|
dockerRun.addArgs({"--entrypoint", "/bin/sh", m_data.imageId});
|
2021-06-17 10:34:46 +02:00
|
|
|
|
|
|
|
|
LOG("RUNNING: " << dockerRun.toUserOutput());
|
2021-09-22 13:08:11 +02:00
|
|
|
QPointer<QtcProcess> shell = new QtcProcess(ProcessMode::Writer);
|
|
|
|
|
connect(shell, &QtcProcess::finished, this, [this, shell] {
|
2021-06-29 13:19:01 +02:00
|
|
|
LOG("\nSHELL FINISHED\n");
|
2021-09-22 13:08:11 +02:00
|
|
|
QTC_ASSERT(shell, return);
|
|
|
|
|
const int exitCode = shell->exitCode();
|
|
|
|
|
LOG("RES: " << shell->result()
|
|
|
|
|
<< " EXIT CODE: " << exitCode
|
|
|
|
|
<< " STDOUT: " << shell->readAllStandardOutput()
|
|
|
|
|
<< " STDERR: " << shell->readAllStandardError());
|
|
|
|
|
// negative exit codes indicate problems like no docker daemon, missing permissions,
|
|
|
|
|
// no shell and seem to result in exit codes 125+
|
|
|
|
|
if (exitCode > 120) {
|
|
|
|
|
DockerPlugin::setGlobalDaemonState(false);
|
|
|
|
|
LOG("DOCKER DAEMON NOT RUNNING?");
|
|
|
|
|
MessageManager::writeFlashing(tr("Docker Daemon appears to be not running. "
|
|
|
|
|
"Verify daemon is up and running and reset the "
|
|
|
|
|
"docker daemon on the docker device settings page "
|
|
|
|
|
"or restart Qt Creator."));
|
2021-06-29 13:19:01 +02:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2021-09-22 13:08:11 +02:00
|
|
|
QTC_ASSERT(!m_shell, delete m_shell);
|
|
|
|
|
m_shell = shell;
|
|
|
|
|
m_shell->setCommand(dockerRun);
|
2021-05-18 06:16:25 +02:00
|
|
|
m_shell->start();
|
|
|
|
|
m_shell->waitForStarted();
|
2021-04-29 07:36:34 +02:00
|
|
|
|
2021-06-17 10:34:46 +02:00
|
|
|
if (m_shell->state() != QProcess::Running) {
|
2021-08-10 16:50:23 +02:00
|
|
|
DockerPlugin::setGlobalDaemonState(false);
|
2021-06-17 10:34:46 +02:00
|
|
|
LOG("DOCKER SHELL FAILED");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-29 07:36:34 +02:00
|
|
|
LOG("CHECKING: " << tempFileName);
|
2021-06-22 12:22:59 +02:00
|
|
|
for (int i = 0; i <= 20; ++i) {
|
2021-04-29 07:36:34 +02:00
|
|
|
QFile file(tempFileName);
|
2021-06-22 12:22:59 +02:00
|
|
|
if (file.open(QIODevice::ReadOnly)) {
|
|
|
|
|
m_container = QString::fromUtf8(file.readAll()).trimmed();
|
|
|
|
|
if (!m_container.isEmpty()) {
|
|
|
|
|
LOG("Container: " << m_container);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2021-04-29 07:36:34 +02:00
|
|
|
}
|
2021-08-10 16:50:23 +02:00
|
|
|
if (i == 20 || !DockerPlugin::isDaemonRunning().value_or(true)) {
|
2021-04-29 07:36:34 +02:00
|
|
|
qWarning("Docker cid file empty.");
|
|
|
|
|
return; // No
|
|
|
|
|
}
|
2021-08-10 16:50:23 +02:00
|
|
|
qApp->processEvents(); // FIXME turn this for-loop into
|
2021-04-29 07:36:34 +02:00
|
|
|
QThread::msleep(100);
|
|
|
|
|
}
|
2021-08-10 16:50:23 +02:00
|
|
|
DockerPlugin::setGlobalDaemonState(true);
|
2021-07-21 12:38:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DockerDevicePrivate::tryCreateLocalFileAccess()
|
|
|
|
|
{
|
2021-08-10 16:50:23 +02:00
|
|
|
if (!m_container.isEmpty() || DockerPlugin::isDaemonRunning().value_or(true) == false)
|
2021-07-21 12:38:04 +02:00
|
|
|
return;
|
|
|
|
|
|
2021-09-23 07:07:09 +02:00
|
|
|
if (!m_shell)
|
|
|
|
|
startContainer();
|
|
|
|
|
|
2021-08-10 16:50:23 +02:00
|
|
|
if (!DockerPlugin::isDaemonRunning().value_or(false))
|
|
|
|
|
return;
|
2021-04-29 07:36:34 +02:00
|
|
|
QtcProcess proc;
|
2021-05-18 06:16:25 +02:00
|
|
|
proc.setCommand({"docker", {"inspect", "--format={{.GraphDriver.Data.MergedDir}}", m_container}});
|
2021-06-29 13:19:01 +02:00
|
|
|
LOG(proc.commandLine().toUserOutput());
|
2021-04-29 07:36:34 +02:00
|
|
|
proc.start();
|
|
|
|
|
proc.waitForFinished();
|
2021-06-10 07:23:35 +02:00
|
|
|
const QString out = proc.stdOut();
|
|
|
|
|
m_mergedDir = out.trimmed();
|
2021-06-29 13:19:01 +02:00
|
|
|
LOG("Found merged dir: " << m_mergedDir);
|
2021-05-18 06:16:25 +02:00
|
|
|
if (m_mergedDir.endsWith('/'))
|
|
|
|
|
m_mergedDir.chop(1);
|
2021-04-29 07:36:34 +02:00
|
|
|
|
2021-06-29 13:19:01 +02:00
|
|
|
if (!QFileInfo(m_mergedDir).isReadable()) {
|
2021-06-10 07:23:35 +02:00
|
|
|
MessageManager::writeFlashing(
|
2021-06-29 13:19:01 +02:00
|
|
|
tr("Local read access to Docker container %1 unavailable through directory \"%2\".")
|
2021-06-10 07:23:35 +02:00
|
|
|
.arg(m_container, m_mergedDir)
|
2021-06-29 13:19:01 +02:00
|
|
|
+ '\n' + tr("Output: '%1'").arg(out)
|
|
|
|
|
+ '\n' + tr("Error: '%1'").arg(proc.stdErr()));
|
2021-07-14 14:13:52 +02:00
|
|
|
if (HostOsInfo::isWindowsHost()) {
|
|
|
|
|
// Disabling merged layer access. This is not supported and anything
|
|
|
|
|
// related to accessing merged layers on Windows fails due to the need
|
|
|
|
|
// of using wsl or a named pipe.
|
|
|
|
|
// TODO investigate how to make it possible nevertheless.
|
2021-07-14 13:25:46 +02:00
|
|
|
m_mergedDir.clear();
|
2021-07-14 14:13:52 +02:00
|
|
|
MessageManager::writeSilently(tr("This is expected on Windows."));
|
2021-07-14 13:25:46 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2021-06-10 07:23:35 +02:00
|
|
|
}
|
|
|
|
|
|
2021-05-18 06:16:25 +02:00
|
|
|
m_mergedDirWatcher.addPath(m_mergedDir);
|
2021-04-29 07:36:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool DockerDevice::hasLocalFileAccess() const
|
|
|
|
|
{
|
|
|
|
|
return !d->m_mergedDir.isEmpty();
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-09 13:18:22 +02:00
|
|
|
void DockerDevice::setMounts(const QStringList &mounts) const
|
|
|
|
|
{
|
|
|
|
|
d->m_data.mounts = mounts;
|
|
|
|
|
d->stopCurrentContainer(); // Force re-start with new mounts.
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-29 07:36:34 +02:00
|
|
|
FilePath DockerDevice::mapToLocalAccess(const FilePath &filePath) const
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(!d->m_mergedDir.isEmpty(), return {});
|
|
|
|
|
QString path = filePath.path();
|
2021-06-29 12:12:59 +02:00
|
|
|
for (const QString &mount : qAsConst(d->m_data.mounts)) {
|
2021-06-17 10:34:46 +02:00
|
|
|
if (path.startsWith(mount + '/'))
|
|
|
|
|
return FilePath::fromString(path);
|
|
|
|
|
}
|
2021-04-29 07:36:34 +02:00
|
|
|
if (path.startsWith('/'))
|
|
|
|
|
return FilePath::fromString(d->m_mergedDir + path);
|
2021-05-31 11:22:51 +02:00
|
|
|
return FilePath::fromString(d->m_mergedDir + '/' + path);
|
2021-04-29 07:36:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FilePath DockerDevice::mapFromLocalAccess(const FilePath &filePath) const
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(!filePath.needsDevice(), return {});
|
|
|
|
|
return mapFromLocalAccess(filePath.toString());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FilePath DockerDevice::mapFromLocalAccess(const QString &filePath) const
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(!d->m_mergedDir.isEmpty(), return {});
|
|
|
|
|
QTC_ASSERT(filePath.startsWith(d->m_mergedDir), return FilePath::fromString(filePath));
|
|
|
|
|
return mapToGlobalPath(FilePath::fromString(filePath.mid(d->m_mergedDir.size())));
|
2021-03-29 09:11:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char DockerDeviceDataImageIdKey[] = "DockerDeviceDataImageId";
|
|
|
|
|
const char DockerDeviceDataRepoKey[] = "DockerDeviceDataRepo";
|
|
|
|
|
const char DockerDeviceDataTagKey[] = "DockerDeviceDataTag";
|
|
|
|
|
const char DockerDeviceDataSizeKey[] = "DockerDeviceDataSize";
|
2021-06-29 10:47:48 +02:00
|
|
|
const char DockerDeviceUseOutsideUser[] = "DockerDeviceUseUidGid";
|
2021-06-29 12:12:59 +02:00
|
|
|
const char DockerDeviceMappedPaths[] = "DockerDeviceMappedPaths";
|
2021-03-29 09:11:36 +02:00
|
|
|
|
|
|
|
|
void DockerDevice::fromMap(const QVariantMap &map)
|
|
|
|
|
{
|
|
|
|
|
ProjectExplorer::IDevice::fromMap(map);
|
2021-04-29 07:36:34 +02:00
|
|
|
d->m_data.imageId = map.value(DockerDeviceDataImageIdKey).toString();
|
|
|
|
|
d->m_data.repo = map.value(DockerDeviceDataRepoKey).toString();
|
|
|
|
|
d->m_data.tag = map.value(DockerDeviceDataTagKey).toString();
|
|
|
|
|
d->m_data.size = map.value(DockerDeviceDataSizeKey).toString();
|
2021-06-29 10:47:48 +02:00
|
|
|
d->m_data.useLocalUidGid = map.value(DockerDeviceUseOutsideUser,
|
|
|
|
|
HostOsInfo::isLinuxHost()).toBool();
|
2021-06-29 12:12:59 +02:00
|
|
|
d->m_data.mounts = map.value(DockerDeviceMappedPaths).toStringList();
|
2021-03-29 09:11:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QVariantMap DockerDevice::toMap() const
|
|
|
|
|
{
|
|
|
|
|
QVariantMap map = ProjectExplorer::IDevice::toMap();
|
2021-04-29 07:36:34 +02:00
|
|
|
map.insert(DockerDeviceDataImageIdKey, d->m_data.imageId);
|
|
|
|
|
map.insert(DockerDeviceDataRepoKey, d->m_data.repo);
|
|
|
|
|
map.insert(DockerDeviceDataTagKey, d->m_data.tag);
|
|
|
|
|
map.insert(DockerDeviceDataSizeKey, d->m_data.size);
|
2021-06-29 10:47:48 +02:00
|
|
|
map.insert(DockerDeviceUseOutsideUser, d->m_data.useLocalUidGid);
|
2021-06-29 12:12:59 +02:00
|
|
|
map.insert(DockerDeviceMappedPaths, d->m_data.mounts);
|
2021-03-29 09:11:36 +02:00
|
|
|
return map;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DeviceProcess *DockerDevice::createProcess(QObject *parent) const
|
|
|
|
|
{
|
|
|
|
|
return new DockerDeviceProcess(sharedFromThis(), parent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool DockerDevice::canAutoDetectPorts() const
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PortsGatheringMethod::Ptr DockerDevice::portsGatheringMethod() const
|
|
|
|
|
{
|
|
|
|
|
return DockerPortsGatheringMethod::Ptr(new DockerPortsGatheringMethod);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DeviceProcessList *DockerDevice::createProcessListModel(QObject *) const
|
|
|
|
|
{
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DeviceTester *DockerDevice::createDeviceTester() const
|
|
|
|
|
{
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DeviceProcessSignalOperation::Ptr DockerDevice::signalOperation() const
|
|
|
|
|
{
|
|
|
|
|
return DeviceProcessSignalOperation::Ptr();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DeviceEnvironmentFetcher::Ptr DockerDevice::environmentFetcher() const
|
|
|
|
|
{
|
|
|
|
|
return DeviceEnvironmentFetcher::Ptr();
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-29 07:36:34 +02:00
|
|
|
FilePath DockerDevice::mapToGlobalPath(const FilePath &pathOnDevice) const
|
|
|
|
|
{
|
2021-05-19 10:56:00 +02:00
|
|
|
if (pathOnDevice.needsDevice()) {
|
|
|
|
|
// Already correct form, only sanity check it's ours...
|
|
|
|
|
QTC_CHECK(handlesFile(pathOnDevice));
|
2021-04-29 07:36:34 +02:00
|
|
|
return pathOnDevice;
|
|
|
|
|
}
|
2021-05-19 10:56:00 +02:00
|
|
|
FilePath result;
|
|
|
|
|
result.setScheme("docker");
|
|
|
|
|
result.setHost(d->m_data.imageId);
|
|
|
|
|
result.setPath(pathOnDevice.path());
|
|
|
|
|
return result;
|
2021-04-29 07:36:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool DockerDevice::handlesFile(const FilePath &filePath) const
|
|
|
|
|
{
|
2021-05-19 10:56:00 +02:00
|
|
|
return filePath.scheme() == "docker" && filePath.host() == d->m_data.imageId;
|
2021-04-29 07:36:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool DockerDevice::isExecutableFile(const FilePath &filePath) const
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(handlesFile(filePath), return false);
|
|
|
|
|
tryCreateLocalFileAccess();
|
|
|
|
|
if (hasLocalFileAccess()) {
|
|
|
|
|
const FilePath localAccess = mapToLocalAccess(filePath);
|
|
|
|
|
const bool res = localAccess.isExecutableFile();
|
|
|
|
|
LOG("Executable? " << filePath.toUserOutput() << localAccess.toUserOutput() << res);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
2021-05-31 16:49:25 +02:00
|
|
|
const QString path = filePath.path();
|
2021-07-20 15:23:26 +02:00
|
|
|
return d->runInShell({"test", {"-x", path}});
|
2021-04-29 07:36:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool DockerDevice::isReadableFile(const FilePath &filePath) const
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(handlesFile(filePath), return false);
|
|
|
|
|
tryCreateLocalFileAccess();
|
|
|
|
|
if (hasLocalFileAccess()) {
|
|
|
|
|
const FilePath localAccess = mapToLocalAccess(filePath);
|
|
|
|
|
const bool res = localAccess.isReadableFile();
|
|
|
|
|
LOG("ReadableFile? " << filePath.toUserOutput() << localAccess.toUserOutput() << res);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
2021-05-31 16:49:25 +02:00
|
|
|
const QString path = filePath.path();
|
2021-07-20 15:23:26 +02:00
|
|
|
return d->runInShell({"test", {"-r", path, "-a", "-f", path}});
|
2021-04-29 07:36:34 +02:00
|
|
|
}
|
|
|
|
|
|
2021-06-22 07:09:00 +02:00
|
|
|
bool DockerDevice::isWritableFile(const Utils::FilePath &filePath) const
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(handlesFile(filePath), return false);
|
|
|
|
|
tryCreateLocalFileAccess();
|
|
|
|
|
if (hasLocalFileAccess()) {
|
|
|
|
|
const FilePath localAccess = mapToLocalAccess(filePath);
|
|
|
|
|
const bool res = localAccess.isWritableFile();
|
|
|
|
|
LOG("WritableFile? " << filePath.toUserOutput() << localAccess.toUserOutput() << res);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
const QString path = filePath.path();
|
2021-07-20 15:23:26 +02:00
|
|
|
return d->runInShell({"test", {"-w", path, "-a", "-f", path}});
|
2021-06-22 07:09:00 +02:00
|
|
|
}
|
|
|
|
|
|
2021-04-29 07:36:34 +02:00
|
|
|
bool DockerDevice::isReadableDirectory(const FilePath &filePath) const
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(handlesFile(filePath), return false);
|
|
|
|
|
tryCreateLocalFileAccess();
|
|
|
|
|
if (hasLocalFileAccess()) {
|
|
|
|
|
const FilePath localAccess = mapToLocalAccess(filePath);
|
|
|
|
|
const bool res = localAccess.isReadableDir();
|
|
|
|
|
LOG("ReadableDirectory? " << filePath.toUserOutput() << localAccess.toUserOutput() << res);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
2021-05-31 16:49:25 +02:00
|
|
|
const QString path = filePath.path();
|
2021-07-20 15:23:26 +02:00
|
|
|
return d->runInShell({"test", {"-r", path, "-a", "-d", path}});
|
2021-04-29 07:36:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool DockerDevice::isWritableDirectory(const FilePath &filePath) const
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(handlesFile(filePath), return false);
|
|
|
|
|
tryCreateLocalFileAccess();
|
|
|
|
|
if (hasLocalFileAccess()) {
|
|
|
|
|
const FilePath localAccess = mapToLocalAccess(filePath);
|
2021-06-21 14:47:02 +02:00
|
|
|
const bool res = localAccess.isWritableDir();
|
2021-04-29 07:36:34 +02:00
|
|
|
LOG("WritableDirectory? " << filePath.toUserOutput() << localAccess.toUserOutput() << res);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
2021-05-31 16:49:25 +02:00
|
|
|
const QString path = filePath.path();
|
2021-07-20 15:23:26 +02:00
|
|
|
return d->runInShell({"test", {"-w", path, "-a", "-d", path}});
|
2021-04-29 07:36:34 +02:00
|
|
|
}
|
|
|
|
|
|
2021-07-14 07:41:23 +02:00
|
|
|
bool DockerDevice::isFile(const FilePath &filePath) const
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(handlesFile(filePath), return false);
|
|
|
|
|
tryCreateLocalFileAccess();
|
|
|
|
|
if (hasLocalFileAccess()) {
|
|
|
|
|
const FilePath localAccess = mapToLocalAccess(filePath);
|
|
|
|
|
const bool res = localAccess.isFile();
|
|
|
|
|
LOG("IsFile? " << filePath.toUserOutput() << localAccess.toUserOutput() << res);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
const QString path = filePath.path();
|
2021-07-20 15:23:26 +02:00
|
|
|
return d->runInShell({"test", {"-f", path}});
|
2021-07-14 07:41:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool DockerDevice::isDirectory(const FilePath &filePath) const
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(handlesFile(filePath), return false);
|
|
|
|
|
tryCreateLocalFileAccess();
|
|
|
|
|
if (hasLocalFileAccess()) {
|
|
|
|
|
const FilePath localAccess = mapToLocalAccess(filePath);
|
|
|
|
|
const bool res = localAccess.isDir();
|
|
|
|
|
LOG("IsDirectory? " << filePath.toUserOutput() << localAccess.toUserOutput() << res);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
const QString path = filePath.path();
|
2021-07-20 15:23:26 +02:00
|
|
|
return d->runInShell({"test", {"-d", path}});
|
2021-07-14 07:41:23 +02:00
|
|
|
}
|
|
|
|
|
|
2021-04-29 07:36:34 +02:00
|
|
|
bool DockerDevice::createDirectory(const FilePath &filePath) const
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(handlesFile(filePath), return false);
|
|
|
|
|
tryCreateLocalFileAccess();
|
|
|
|
|
if (hasLocalFileAccess()) {
|
|
|
|
|
const FilePath localAccess = mapToLocalAccess(filePath);
|
|
|
|
|
const bool res = localAccess.createDir();
|
|
|
|
|
LOG("CreateDirectory? " << filePath.toUserOutput() << localAccess.toUserOutput() << res);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
2021-05-31 16:49:25 +02:00
|
|
|
const QString path = filePath.path();
|
2021-07-15 10:10:47 +02:00
|
|
|
return d->runInContainer({"mkdir", {"-p", path}});
|
2021-04-29 07:36:34 +02:00
|
|
|
}
|
|
|
|
|
|
2021-06-01 09:40:17 +02:00
|
|
|
bool DockerDevice::exists(const FilePath &filePath) const
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(handlesFile(filePath), return false);
|
|
|
|
|
tryCreateLocalFileAccess();
|
|
|
|
|
if (hasLocalFileAccess()) {
|
|
|
|
|
const FilePath localAccess = mapToLocalAccess(filePath);
|
|
|
|
|
const bool res = localAccess.exists();
|
|
|
|
|
LOG("Exists? " << filePath.toUserOutput() << localAccess.toUserOutput() << res);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
const QString path = filePath.path();
|
2021-07-20 15:23:26 +02:00
|
|
|
return d->runInShell({"test", {"-e", path}});
|
2021-06-01 09:40:17 +02:00
|
|
|
}
|
|
|
|
|
|
2021-06-29 09:41:53 +02:00
|
|
|
bool DockerDevice::ensureExistingFile(const FilePath &filePath) const
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(handlesFile(filePath), return false);
|
|
|
|
|
tryCreateLocalFileAccess();
|
|
|
|
|
if (hasLocalFileAccess()) {
|
|
|
|
|
const FilePath localAccess = mapToLocalAccess(filePath);
|
|
|
|
|
const bool res = localAccess.ensureExistingFile();
|
|
|
|
|
LOG("Ensure existing file? " << filePath.toUserOutput() << localAccess.toUserOutput() << res);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
const QString path = filePath.path();
|
2021-07-20 15:23:26 +02:00
|
|
|
return d->runInShell({"touch", {path}});
|
2021-06-29 09:41:53 +02:00
|
|
|
}
|
|
|
|
|
|
2021-06-14 08:33:44 +02:00
|
|
|
bool DockerDevice::removeFile(const FilePath &filePath) const
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(handlesFile(filePath), return false);
|
|
|
|
|
tryCreateLocalFileAccess();
|
|
|
|
|
if (hasLocalFileAccess()) {
|
|
|
|
|
const FilePath localAccess = mapToLocalAccess(filePath);
|
|
|
|
|
const bool res = localAccess.removeFile();
|
|
|
|
|
LOG("Remove? " << filePath.toUserOutput() << localAccess.toUserOutput() << res);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
2021-07-15 10:10:47 +02:00
|
|
|
return d->runInContainer({"rm", {filePath.path()}});
|
2021-06-14 08:33:44 +02:00
|
|
|
}
|
|
|
|
|
|
2021-06-28 15:26:44 +02:00
|
|
|
bool DockerDevice::removeRecursively(const FilePath &filePath) const
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(handlesFile(filePath), return false);
|
|
|
|
|
QTC_ASSERT(filePath.path().startsWith('/'), return false);
|
|
|
|
|
tryCreateLocalFileAccess();
|
|
|
|
|
if (hasLocalFileAccess()) {
|
|
|
|
|
const FilePath localAccess = mapToLocalAccess(filePath);
|
|
|
|
|
const bool res = localAccess.removeRecursively();
|
|
|
|
|
LOG("Remove recursively? " << filePath.toUserOutput() << localAccess.toUserOutput() << res);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
2021-07-16 07:42:59 +02:00
|
|
|
|
|
|
|
|
const QString path = filePath.cleanPath().path();
|
|
|
|
|
// We are expecting this only to be called in a context of build directories or similar.
|
|
|
|
|
// Chicken out in some cases that _might_ be user code errors.
|
|
|
|
|
QTC_ASSERT(path.startsWith('/'), return false);
|
2021-07-22 10:33:23 +02:00
|
|
|
const int levelsNeeded = path.startsWith("/home/") ? 4 : 3;
|
2021-07-16 07:42:59 +02:00
|
|
|
QTC_ASSERT(path.count('/') >= levelsNeeded, return false);
|
|
|
|
|
|
|
|
|
|
return d->runInContainer({"rm", {"-rf", "--", path}});
|
2021-06-28 15:26:44 +02:00
|
|
|
}
|
|
|
|
|
|
2021-06-14 08:33:44 +02:00
|
|
|
bool DockerDevice::copyFile(const FilePath &filePath, const FilePath &target) const
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(handlesFile(filePath), return false);
|
|
|
|
|
QTC_ASSERT(handlesFile(target), return false);
|
|
|
|
|
tryCreateLocalFileAccess();
|
|
|
|
|
if (hasLocalFileAccess()) {
|
|
|
|
|
const FilePath localAccess = mapToLocalAccess(filePath);
|
|
|
|
|
const FilePath localTarget = mapToLocalAccess(target);
|
|
|
|
|
const bool res = localAccess.copyFile(localTarget);
|
|
|
|
|
LOG("Copy " << filePath.toUserOutput() << localAccess.toUserOutput() << localTarget << res);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
2021-07-15 10:10:47 +02:00
|
|
|
return d->runInContainer({"cp", {filePath.path(), target.path()}});
|
2021-06-14 08:33:44 +02:00
|
|
|
}
|
|
|
|
|
|
2021-06-28 13:57:10 +02:00
|
|
|
bool DockerDevice::renameFile(const FilePath &filePath, const FilePath &target) const
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(handlesFile(filePath), return false);
|
|
|
|
|
QTC_ASSERT(handlesFile(target), return false);
|
|
|
|
|
tryCreateLocalFileAccess();
|
|
|
|
|
if (hasLocalFileAccess()) {
|
|
|
|
|
const FilePath localAccess = mapToLocalAccess(filePath);
|
|
|
|
|
const FilePath localTarget = mapToLocalAccess(target);
|
|
|
|
|
const bool res = localAccess.renameFile(localTarget);
|
|
|
|
|
LOG("Move " << filePath.toUserOutput() << localAccess.toUserOutput() << localTarget << res);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
2021-07-15 10:10:47 +02:00
|
|
|
return d->runInContainer({"mv", {filePath.path(), target.path()}});
|
2021-06-28 13:57:10 +02:00
|
|
|
}
|
|
|
|
|
|
2021-06-14 08:33:44 +02:00
|
|
|
QDateTime DockerDevice::lastModified(const FilePath &filePath) const
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(handlesFile(filePath), return {});
|
|
|
|
|
tryCreateLocalFileAccess();
|
|
|
|
|
if (hasLocalFileAccess()) {
|
|
|
|
|
const FilePath localAccess = mapToLocalAccess(filePath);
|
|
|
|
|
const QDateTime res = localAccess.lastModified();
|
|
|
|
|
LOG("Last modified? " << filePath.toUserOutput() << localAccess.toUserOutput() << res);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
2021-07-16 07:35:44 +02:00
|
|
|
|
|
|
|
|
QtcProcess proc;
|
|
|
|
|
proc.setCommand({"stat", {"-c", "%Y", filePath.path()}});
|
|
|
|
|
runProcess(proc);
|
|
|
|
|
proc.waitForFinished();
|
|
|
|
|
|
|
|
|
|
const qint64 secs = proc.rawStdOut().toLongLong();
|
|
|
|
|
const QDateTime dt = QDateTime::fromSecsSinceEpoch(secs, Qt::UTC);
|
|
|
|
|
return dt;
|
2021-06-14 08:33:44 +02:00
|
|
|
}
|
|
|
|
|
|
2021-06-29 15:57:41 +02:00
|
|
|
FilePath DockerDevice::symLinkTarget(const FilePath &filePath) const
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(handlesFile(filePath), return {});
|
|
|
|
|
tryCreateLocalFileAccess();
|
|
|
|
|
if (hasLocalFileAccess()) {
|
|
|
|
|
const FilePath localAccess = mapToLocalAccess(filePath);
|
|
|
|
|
const FilePath target = localAccess.symLinkTarget();
|
|
|
|
|
LOG("SymLinkTarget? " << filePath.toUserOutput() << localAccess.toUserOutput() << target);
|
|
|
|
|
if (target.isEmpty())
|
|
|
|
|
return {};
|
|
|
|
|
return mapToGlobalPath(target);
|
|
|
|
|
}
|
2021-07-19 13:45:31 +02:00
|
|
|
|
|
|
|
|
const QString output = d->outputForRunInShell({"readlink", {"-n", "-e", filePath.path()}});
|
|
|
|
|
return output.isEmpty() ? FilePath() : filePath.withNewPath(output);
|
2021-06-07 15:47:06 +02:00
|
|
|
}
|
|
|
|
|
|
2021-07-23 12:06:38 +02:00
|
|
|
qint64 DockerDevice::fileSize(const FilePath &filePath) const
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(handlesFile(filePath), return -1);
|
|
|
|
|
tryCreateLocalFileAccess();
|
|
|
|
|
if (hasLocalFileAccess()) {
|
|
|
|
|
const FilePath localAccess = mapToLocalAccess(filePath);
|
|
|
|
|
LOG("File size? " << filePath.toUserOutput() << localAccess.toUserOutput() << localAccess.fileSize());
|
|
|
|
|
return localAccess.fileSize();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const QString output = d->outputForRunInShell({"stat", {"-c", "%s", filePath.path()}});
|
|
|
|
|
return output.toLongLong();
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-23 17:55:25 +02:00
|
|
|
QFileDevice::Permissions DockerDevice::permissions(const FilePath &filePath) const
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(handlesFile(filePath), return {});
|
|
|
|
|
tryCreateLocalFileAccess();
|
|
|
|
|
if (hasLocalFileAccess()) {
|
|
|
|
|
const FilePath localAccess = mapToLocalAccess(filePath);
|
|
|
|
|
LOG("Permissions? " << filePath.toUserOutput() << localAccess.toUserOutput() << localAccess.permissions());
|
|
|
|
|
return localAccess.permissions();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const QString output = d->outputForRunInShell({"stat", {"-c", "%a", filePath.path()}});
|
|
|
|
|
const uint bits = output.toUInt(nullptr, 8);
|
|
|
|
|
QFileDevice::Permissions perm = {};
|
|
|
|
|
#define BIT(n, p) if (bits & (1<<n)) perm |= QFileDevice::p
|
|
|
|
|
BIT(0, ExeOther);
|
|
|
|
|
BIT(1, WriteOther);
|
|
|
|
|
BIT(2, ReadOther);
|
|
|
|
|
BIT(3, ExeGroup);
|
|
|
|
|
BIT(4, WriteGroup);
|
|
|
|
|
BIT(5, ReadGroup);
|
|
|
|
|
BIT(6, ExeUser);
|
|
|
|
|
BIT(7, WriteUser);
|
|
|
|
|
BIT(8, ReadUser);
|
|
|
|
|
#undef BIT
|
|
|
|
|
return perm;
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-12 10:29:18 +02:00
|
|
|
bool DockerDevice::setPermissions(const FilePath &filePath, QFileDevice::Permissions permissions) const
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(handlesFile(filePath), return {});
|
|
|
|
|
tryCreateLocalFileAccess();
|
|
|
|
|
if (hasLocalFileAccess()) {
|
|
|
|
|
const FilePath localAccess = mapToLocalAccess(filePath);
|
|
|
|
|
LOG("Set permissions? " << filePath.toUserOutput() << localAccess.toUserOutput() << localAccess.permissions());
|
|
|
|
|
return localAccess.setPermissions(permissions);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QTC_CHECK(false); // FIXME: Implement.
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-14 14:14:15 +02:00
|
|
|
FilePaths DockerDevice::findFilesWithFind(const FilePath &filePath,
|
|
|
|
|
const QStringList &nameFilters,
|
|
|
|
|
QDir::Filters filters,
|
|
|
|
|
QDir::SortFlags sort) const
|
|
|
|
|
{
|
|
|
|
|
QTC_CHECK(filePath.isAbsolutePath());
|
|
|
|
|
QStringList arguments{filePath.path(), "-maxdepth", "1"};
|
|
|
|
|
if (filters & QDir::NoSymLinks)
|
|
|
|
|
arguments.prepend("-H");
|
|
|
|
|
else
|
|
|
|
|
arguments.prepend("-L");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
QStringList filterOptions;
|
|
|
|
|
if (filters & QDir::Dirs)
|
|
|
|
|
filterOptions << "-type" << "d";
|
|
|
|
|
if (filters & QDir::Files) {
|
|
|
|
|
if (!filterOptions.isEmpty())
|
|
|
|
|
filterOptions << "-o";
|
|
|
|
|
filterOptions << "-type" << "f";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (filters & QDir::Readable)
|
|
|
|
|
filterOptions << "-readable";
|
|
|
|
|
if (filters & QDir::Writable)
|
|
|
|
|
filterOptions << "-writable";
|
|
|
|
|
if (filters & QDir::Executable)
|
|
|
|
|
filterOptions << "-executable";
|
|
|
|
|
|
|
|
|
|
QTC_CHECK(filters ^ QDir::AllDirs);
|
|
|
|
|
QTC_CHECK(filters ^ QDir::Drives);
|
|
|
|
|
QTC_CHECK(filters ^ QDir::NoDot);
|
|
|
|
|
QTC_CHECK(filters ^ QDir::NoDotDot);
|
|
|
|
|
QTC_CHECK(filters ^ QDir::Hidden);
|
|
|
|
|
QTC_CHECK(filters ^ QDir::System);
|
|
|
|
|
|
|
|
|
|
const QString nameOption = (filters & QDir::CaseSensitive) ? QString{"-name"}
|
|
|
|
|
: QString{"-iname"};
|
|
|
|
|
if (!nameFilters.isEmpty()) {
|
|
|
|
|
filterOptions << nameOption << nameFilters.first();
|
|
|
|
|
const QRegularExpression oneChar("\\[.*?\\]");
|
|
|
|
|
for (int i = 1, len = nameFilters.size(); i < len; ++i) {
|
|
|
|
|
QString current = nameFilters.at(i);
|
|
|
|
|
current.replace(oneChar, "?"); // BAD! but still better than nothing
|
|
|
|
|
filterOptions << "-o" << nameOption << current;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
arguments << filterOptions;
|
|
|
|
|
const QString output = d->outputForRunInShell({"find", arguments});
|
|
|
|
|
if (!output.isEmpty() && !output.startsWith(filePath.path())) { // missing find, unknown option
|
|
|
|
|
LOG("Setting 'do not use find'" << output.left(output.indexOf('\n')));
|
|
|
|
|
d->m_useFind = false;
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QStringList entries = output.split("\n", Qt::SkipEmptyParts);
|
|
|
|
|
if (sort & QDir::Name)
|
|
|
|
|
entries.sort(filters & QDir::CaseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive);
|
|
|
|
|
QTC_CHECK(sort == QDir::Name || sort == QDir::NoSort || sort == QDir::Unsorted);
|
|
|
|
|
|
|
|
|
|
// strip out find messages
|
|
|
|
|
entries = Utils::filtered(entries,
|
|
|
|
|
[](const QString &entry) { return !entry.startsWith("find: "); });
|
|
|
|
|
FilePaths result;
|
|
|
|
|
for (const QString &entry : qAsConst(entries))
|
|
|
|
|
result.append(FilePath::fromString(entry).onDevice(filePath));
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-22 14:08:53 +02:00
|
|
|
static FilePaths filterEntriesHelper(const FilePath &base,
|
|
|
|
|
const QStringList &entries,
|
|
|
|
|
const QStringList &nameFilters,
|
|
|
|
|
QDir::Filters filters,
|
|
|
|
|
QDir::SortFlags sort)
|
|
|
|
|
{
|
|
|
|
|
const QList<QRegularExpression> nameRegexps = transform(nameFilters, [](const QString &filter) {
|
|
|
|
|
QRegularExpression re;
|
|
|
|
|
re.setPattern(QRegularExpression::wildcardToRegularExpression(filter));
|
|
|
|
|
QTC_CHECK(re.isValid());
|
|
|
|
|
return re;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const auto nameMatches = [&nameRegexps](const QString &fileName) {
|
|
|
|
|
for (const QRegularExpression &re : nameRegexps) {
|
|
|
|
|
const QRegularExpressionMatch match = re.match(fileName);
|
|
|
|
|
if (match.hasMatch())
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2021-08-05 15:01:27 +02:00
|
|
|
return nameRegexps.isEmpty();
|
2021-07-22 14:08:53 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// FIXME: Handle sort and filters. For now bark on unsupported options.
|
|
|
|
|
QTC_CHECK(filters == QDir::NoFilter);
|
|
|
|
|
QTC_CHECK(sort == QDir::NoSort);
|
|
|
|
|
|
|
|
|
|
FilePaths result;
|
|
|
|
|
for (const QString &entry : entries) {
|
|
|
|
|
if (!nameMatches(entry))
|
|
|
|
|
continue;
|
|
|
|
|
result.append(base.pathAppended(entry));
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-05 13:39:18 +02:00
|
|
|
FilePaths DockerDevice::directoryEntries(const FilePath &filePath,
|
|
|
|
|
const QStringList &nameFilters,
|
|
|
|
|
QDir::Filters filters,
|
|
|
|
|
QDir::SortFlags sort) const
|
2021-04-29 07:36:34 +02:00
|
|
|
{
|
|
|
|
|
QTC_ASSERT(handlesFile(filePath), return {});
|
|
|
|
|
tryCreateLocalFileAccess();
|
2021-07-05 13:39:18 +02:00
|
|
|
if (hasLocalFileAccess()) {
|
|
|
|
|
const FilePaths entries = mapToLocalAccess(filePath).dirEntries(nameFilters, filters, sort);
|
|
|
|
|
return Utils::transform(entries, [this](const FilePath &entry) {
|
|
|
|
|
return mapFromLocalAccess(entry);
|
|
|
|
|
});
|
|
|
|
|
}
|
2021-06-29 11:10:27 +02:00
|
|
|
|
2021-09-14 14:14:15 +02:00
|
|
|
if (d->m_useFind) {
|
|
|
|
|
const FilePaths result = findFilesWithFind(filePath, nameFilters, filters, sort);
|
|
|
|
|
if (d->m_useFind)
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// if we do not have find - use ls as fallback
|
2021-07-20 15:23:26 +02:00
|
|
|
const QString output = d->outputForRunInShell({"ls", {"-1", "-b", "--", filePath.path()}});
|
|
|
|
|
QStringList entries = output.split('\n', Qt::SkipEmptyParts);
|
2021-07-22 14:08:53 +02:00
|
|
|
return filterEntriesHelper(filePath, entries, nameFilters, filters, sort);
|
2021-04-29 07:36:34 +02:00
|
|
|
}
|
|
|
|
|
|
2021-07-09 08:30:11 +02:00
|
|
|
QByteArray DockerDevice::fileContents(const FilePath &filePath, qint64 limit, qint64 offset) const
|
2021-04-29 07:36:34 +02:00
|
|
|
{
|
|
|
|
|
QTC_ASSERT(handlesFile(filePath), return {});
|
|
|
|
|
tryCreateLocalFileAccess();
|
|
|
|
|
if (hasLocalFileAccess())
|
2021-07-09 08:30:11 +02:00
|
|
|
return mapToLocalAccess(filePath).fileContents(limit, offset);
|
2021-04-29 07:36:34 +02:00
|
|
|
|
2021-07-15 10:51:39 +02:00
|
|
|
QStringList args = {"if=" + filePath.path(), "status=none"};
|
|
|
|
|
if (limit > 0 || offset > 0) {
|
|
|
|
|
const qint64 gcd = std::gcd(limit, offset);
|
|
|
|
|
args += {QString("bs=%1").arg(gcd),
|
|
|
|
|
QString("count=%1").arg(limit / gcd),
|
|
|
|
|
QString("seek=%1").arg(offset / gcd)};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QtcProcess proc;
|
|
|
|
|
proc.setCommand({"dd", args});
|
|
|
|
|
runProcess(proc);
|
|
|
|
|
proc.waitForFinished();
|
|
|
|
|
|
|
|
|
|
QByteArray output = proc.readAllStandardOutput();
|
|
|
|
|
return output;
|
2021-04-29 07:36:34 +02:00
|
|
|
}
|
|
|
|
|
|
2021-07-16 08:34:04 +02:00
|
|
|
bool DockerDevice::writeFileContents(const FilePath &filePath, const QByteArray &data) const
|
2021-06-15 18:29:51 +02:00
|
|
|
{
|
|
|
|
|
QTC_ASSERT(handlesFile(filePath), return {});
|
|
|
|
|
tryCreateLocalFileAccess();
|
|
|
|
|
if (hasLocalFileAccess())
|
|
|
|
|
return mapToLocalAccess(filePath).writeFileContents(data);
|
|
|
|
|
|
2021-07-16 08:34:04 +02:00
|
|
|
// This following would be the generic Unix solution.
|
|
|
|
|
// But it doesn't pass input. FIXME: Why?
|
|
|
|
|
// QtcProcess proc;
|
|
|
|
|
// proc.setCommand({"dd", {"of=" + filePath.path()}});
|
|
|
|
|
// proc.setWriteData(data);
|
|
|
|
|
// runProcess(proc);
|
|
|
|
|
// proc.waitForFinished();
|
|
|
|
|
|
|
|
|
|
TemporaryFile tempFile("dockertransport-XXXXXX");
|
|
|
|
|
tempFile.open();
|
|
|
|
|
tempFile.write(data);
|
|
|
|
|
|
|
|
|
|
const QString tempName = tempFile.fileName();
|
|
|
|
|
tempFile.close();
|
|
|
|
|
|
|
|
|
|
CommandLine cmd{"docker", {"cp", tempName, d->m_container + ':' + filePath.path()}};
|
|
|
|
|
|
|
|
|
|
QtcProcess proc;
|
|
|
|
|
proc.setCommand(cmd);
|
|
|
|
|
proc.runBlocking();
|
|
|
|
|
|
|
|
|
|
return proc.exitCode() == 0;
|
2021-06-15 18:29:51 +02:00
|
|
|
}
|
|
|
|
|
|
2021-04-29 07:36:34 +02:00
|
|
|
void DockerDevice::runProcess(QtcProcess &process) const
|
|
|
|
|
{
|
2021-06-28 15:48:18 +02:00
|
|
|
tryCreateLocalFileAccess();
|
2021-08-10 16:50:23 +02:00
|
|
|
if (!DockerPlugin::isDaemonRunning().value_or(false))
|
|
|
|
|
return;
|
|
|
|
|
if (d->m_container.isEmpty()) {
|
2021-06-28 15:48:18 +02:00
|
|
|
LOG("No container set to run " << process.commandLine().toUserOutput());
|
|
|
|
|
QTC_CHECK(false);
|
|
|
|
|
process.setResult(QtcProcess::StartFailed);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-03 10:39:15 +02:00
|
|
|
const FilePath workingDir = process.workingDirectory();
|
2021-07-23 11:13:59 +02:00
|
|
|
const Environment env = process.environment();
|
2021-04-29 07:36:34 +02:00
|
|
|
|
2021-05-25 13:23:57 +02:00
|
|
|
CommandLine cmd{"docker", {"exec"}};
|
2021-09-09 09:54:11 +02:00
|
|
|
if (!workingDir.isEmpty()) {
|
2021-06-29 08:38:36 +02:00
|
|
|
cmd.addArgs({"-w", workingDir.path()});
|
2021-09-09 09:54:11 +02:00
|
|
|
if (QTC_GUARD(workingDir.needsDevice())) // warn on local working directory for docker cmd
|
|
|
|
|
process.setWorkingDirectory(FileUtils::homePath()); // reset working dir for docker exec
|
|
|
|
|
}
|
2021-08-07 11:41:23 +02:00
|
|
|
if (process.processMode() == ProcessMode::Writer)
|
2021-06-21 15:13:22 +02:00
|
|
|
cmd.addArg("-i");
|
2021-09-06 16:19:31 +02:00
|
|
|
if (env.size() != 0 && !hasLocalFileAccess()) {
|
2021-07-23 11:13:59 +02:00
|
|
|
process.unsetEnvironment();
|
|
|
|
|
// FIXME the below would be probably correct if the respective tools would use correct
|
|
|
|
|
// environment already, but most are using the host environment which usually makes
|
|
|
|
|
// no sense on the device and may degrade performance
|
|
|
|
|
// const QStringList envList = env.toStringList();
|
|
|
|
|
// for (const QString &keyValue : envList) {
|
|
|
|
|
// cmd.addArg("-e");
|
|
|
|
|
// cmd.addArg(keyValue);
|
|
|
|
|
// }
|
|
|
|
|
}
|
2021-05-25 13:23:57 +02:00
|
|
|
cmd.addArg(d->m_container);
|
2021-08-12 13:04:22 +02:00
|
|
|
cmd.addCommandLineAsArgs(process.commandLine());
|
2021-04-29 07:36:34 +02:00
|
|
|
|
2021-06-03 10:39:15 +02:00
|
|
|
LOG("Run" << cmd.toUserOutput() << " in " << workingDir.toUserOutput());
|
2021-05-25 13:23:57 +02:00
|
|
|
|
|
|
|
|
process.setCommand(cmd);
|
2021-04-29 07:36:34 +02:00
|
|
|
process.start();
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-26 17:18:40 +02:00
|
|
|
Environment DockerDevice::systemEnvironment() const
|
|
|
|
|
{
|
|
|
|
|
if (d->m_cachedEnviroment.size() == 0)
|
|
|
|
|
d->fetchSystemEnviroment();
|
|
|
|
|
|
|
|
|
|
QTC_CHECK(d->m_cachedEnviroment.size() != 0);
|
|
|
|
|
return d->m_cachedEnviroment;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-31 10:10:53 +02:00
|
|
|
void DockerDevice::aboutToBeRemoved() const
|
|
|
|
|
{
|
2021-07-01 17:59:15 +02:00
|
|
|
KitDetector detector(sharedFromThis());
|
|
|
|
|
detector.undoAutoDetect(d->m_data.id());
|
2021-05-31 10:10:53 +02:00
|
|
|
}
|
|
|
|
|
|
2021-05-26 17:18:40 +02:00
|
|
|
void DockerDevicePrivate::fetchSystemEnviroment()
|
|
|
|
|
{
|
2021-06-22 04:33:47 +02:00
|
|
|
QtcProcess proc;
|
2021-05-26 17:18:40 +02:00
|
|
|
proc.setCommand({"env", {}});
|
|
|
|
|
|
2021-06-21 12:08:10 +02:00
|
|
|
q->runProcess(proc); // FIXME: This only starts.
|
|
|
|
|
proc.waitForFinished();
|
2021-05-26 17:18:40 +02:00
|
|
|
|
|
|
|
|
const QString remoteOutput = proc.stdOut();
|
|
|
|
|
m_cachedEnviroment = Environment(remoteOutput.split('\n', Qt::SkipEmptyParts), q->osType());
|
2021-08-19 05:56:25 +02:00
|
|
|
|
|
|
|
|
const QString remoteError = proc.stdErr();
|
|
|
|
|
if (!remoteError.isEmpty())
|
|
|
|
|
qWarning("Cannot read container environment: %s\n", qPrintable(remoteError));
|
2021-05-26 17:18:40 +02:00
|
|
|
}
|
|
|
|
|
|
2021-07-15 10:10:47 +02:00
|
|
|
bool DockerDevicePrivate::runInContainer(const CommandLine &cmd) const
|
2021-04-29 07:36:34 +02:00
|
|
|
{
|
2021-08-10 16:50:23 +02:00
|
|
|
if (!DockerPlugin::isDaemonRunning().value_or(false))
|
2021-07-15 10:10:47 +02:00
|
|
|
return false;
|
2021-04-29 15:57:56 +02:00
|
|
|
CommandLine dcmd{"docker", {"exec", m_container}};
|
2021-08-12 13:04:22 +02:00
|
|
|
dcmd.addCommandLineAsArgs(cmd);
|
2021-04-29 07:36:34 +02:00
|
|
|
|
|
|
|
|
QtcProcess proc;
|
|
|
|
|
proc.setCommand(dcmd);
|
2021-06-29 14:26:20 +02:00
|
|
|
proc.setWorkingDirectory(QDir::tempPath());
|
2021-04-29 07:36:34 +02:00
|
|
|
proc.start();
|
2021-05-06 15:38:12 +02:00
|
|
|
proc.waitForFinished();
|
2021-04-29 07:36:34 +02:00
|
|
|
|
|
|
|
|
LOG("Run sync:" << dcmd.toUserOutput() << " result: " << proc.exitCode());
|
2021-07-15 10:10:47 +02:00
|
|
|
const int exitCode = proc.exitCode();
|
|
|
|
|
return exitCode == 0;
|
2021-04-29 07:36:34 +02:00
|
|
|
}
|
|
|
|
|
|
2021-07-20 15:23:26 +02:00
|
|
|
bool DockerDevicePrivate::runInShell(const CommandLine &cmd) const
|
|
|
|
|
{
|
2021-09-22 13:08:11 +02:00
|
|
|
if (!QTC_GUARD(DockerPlugin::isDaemonRunning().value_or(false))) {
|
|
|
|
|
LOG("No daemon. Could not run " << cmd.toUserOutput());
|
2021-07-20 15:23:26 +02:00
|
|
|
return false;
|
2021-09-22 13:08:11 +02:00
|
|
|
}
|
|
|
|
|
QTC_ASSERT(m_shell, LOG("No shell. Could not run " << cmd.toUserOutput()); return false);
|
2021-07-20 15:23:26 +02:00
|
|
|
QMutexLocker l(&m_shellMutex);
|
|
|
|
|
m_shell->readAllStandardOutput(); // clean possible left-overs
|
|
|
|
|
m_shell->write(cmd.toUserOutput().toUtf8() + "\necho $?\n");
|
|
|
|
|
m_shell->waitForReadyRead();
|
|
|
|
|
QByteArray output = m_shell->readAllStandardOutput();
|
|
|
|
|
int result = output.toInt();
|
|
|
|
|
LOG("Run command in shell:" << cmd.toUserOutput() << "result: " << output << " ==>" << result);
|
|
|
|
|
return result == 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// generate hex value
|
|
|
|
|
static QByteArray randomHex()
|
|
|
|
|
{
|
|
|
|
|
quint32 val = QRandomGenerator::global()->generate();
|
|
|
|
|
return QString::number(val, 16).toUtf8();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString DockerDevicePrivate::outputForRunInShell(const CommandLine &cmd) const
|
|
|
|
|
{
|
2021-08-10 16:50:23 +02:00
|
|
|
if (!DockerPlugin::isDaemonRunning().value_or(false))
|
2021-07-20 15:23:26 +02:00
|
|
|
return {};
|
|
|
|
|
QTC_ASSERT(m_shell, return {});
|
|
|
|
|
QMutexLocker l(&m_shellMutex);
|
|
|
|
|
m_shell->readAllStandardOutput(); // clean possible left-overs
|
|
|
|
|
const QByteArray markerWithNewLine("___QC_DOCKER_" + randomHex() + "_OUTPUT_MARKER___\n");
|
|
|
|
|
m_shell->write(cmd.toUserOutput().toUtf8() + "\necho -n \"" + markerWithNewLine + "\"\n");
|
|
|
|
|
QByteArray output;
|
|
|
|
|
while (!output.endsWith(markerWithNewLine)) {
|
|
|
|
|
m_shell->waitForReadyRead();
|
|
|
|
|
output.append(m_shell->readAllStandardOutput());
|
|
|
|
|
}
|
|
|
|
|
LOG("Run command in shell:" << cmd.toUserOutput() << "output size:" << output.size());
|
|
|
|
|
if (QTC_GUARD(output.endsWith(markerWithNewLine)))
|
|
|
|
|
output.chop(markerWithNewLine.size());
|
|
|
|
|
return QString::fromUtf8(output);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-29 09:11:36 +02:00
|
|
|
// Factory
|
|
|
|
|
|
|
|
|
|
DockerDeviceFactory::DockerDeviceFactory()
|
|
|
|
|
: IDeviceFactory(Constants::DOCKER_DEVICE_TYPE)
|
|
|
|
|
{
|
|
|
|
|
setDisplayName(DockerDevice::tr("Docker Device"));
|
|
|
|
|
setIcon(QIcon());
|
|
|
|
|
setCanCreate(true);
|
|
|
|
|
setConstructionFunction([] { return DockerDevice::create({}); });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class DockerImageItem final : public TreeItem, public DockerDeviceData
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
DockerImageItem() {}
|
|
|
|
|
|
|
|
|
|
QVariant data(int column, int role) const final
|
|
|
|
|
{
|
|
|
|
|
switch (column) {
|
|
|
|
|
case 0:
|
|
|
|
|
if (role == Qt::DisplayRole)
|
|
|
|
|
return imageId;
|
|
|
|
|
break;
|
|
|
|
|
case 1:
|
|
|
|
|
if (role == Qt::DisplayRole)
|
|
|
|
|
return repo;
|
|
|
|
|
break;
|
|
|
|
|
case 2:
|
|
|
|
|
if (role == Qt::DisplayRole)
|
|
|
|
|
return tag;
|
|
|
|
|
break;
|
|
|
|
|
case 3:
|
|
|
|
|
if (role == Qt::DisplayRole)
|
|
|
|
|
return size;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return QVariant();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class DockerDeviceSetupWizard final : public QDialog
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
DockerDeviceSetupWizard()
|
|
|
|
|
: QDialog(ICore::dialogParent())
|
|
|
|
|
{
|
2021-07-09 14:51:22 +02:00
|
|
|
setWindowTitle(DockerDevice::tr("Docker Image Selection"));
|
2021-04-29 07:36:34 +02:00
|
|
|
resize(800, 600);
|
2021-03-29 09:11:36 +02:00
|
|
|
|
|
|
|
|
m_model.setHeader({"Image", "Repository", "Tag", "Size"});
|
|
|
|
|
|
|
|
|
|
m_view = new TreeView;
|
2021-04-29 07:36:34 +02:00
|
|
|
m_view->setModel(&m_model);
|
|
|
|
|
m_view->header()->setStretchLastSection(true);
|
|
|
|
|
m_view->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
2021-03-29 09:11:36 +02:00
|
|
|
m_view->setSelectionBehavior(QAbstractItemView::SelectRows);
|
|
|
|
|
m_view->setSelectionMode(QAbstractItemView::SingleSelection);
|
|
|
|
|
|
2021-05-31 10:10:53 +02:00
|
|
|
m_log = new QTextBrowser;
|
|
|
|
|
m_log->setVisible(false);
|
2021-03-29 09:11:36 +02:00
|
|
|
|
2021-05-31 10:10:53 +02:00
|
|
|
m_buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
2021-03-29 09:11:36 +02:00
|
|
|
|
|
|
|
|
using namespace Layouting;
|
|
|
|
|
Column {
|
|
|
|
|
m_view,
|
2021-05-31 10:10:53 +02:00
|
|
|
m_log,
|
|
|
|
|
m_buttons,
|
2021-03-29 09:11:36 +02:00
|
|
|
}.attachTo(this);
|
|
|
|
|
|
2021-05-31 10:10:53 +02:00
|
|
|
connect(m_buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
|
|
|
|
connect(m_buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
|
|
|
|
m_buttons->button(QDialogButtonBox::Ok)->setEnabled(false);
|
2021-03-29 09:11:36 +02:00
|
|
|
|
|
|
|
|
CommandLine cmd{"docker", {"images", "--format", "{{.ID}}\\t{{.Repository}}\\t{{.Tag}}\\t{{.Size}}"}};
|
2021-07-09 14:51:22 +02:00
|
|
|
m_log->append(DockerDevice::tr("Running \"%1\"\n").arg(cmd.toUserOutput()));
|
2021-03-29 09:11:36 +02:00
|
|
|
|
|
|
|
|
m_process = new QtcProcess(this);
|
|
|
|
|
m_process->setCommand(cmd);
|
|
|
|
|
|
2021-05-31 10:10:53 +02:00
|
|
|
connect(m_process, &QtcProcess::readyReadStandardOutput, [this] {
|
2021-03-29 09:11:36 +02:00
|
|
|
const QString out = QString::fromUtf8(m_process->readAllStandardOutput().trimmed());
|
2021-05-31 10:10:53 +02:00
|
|
|
m_log->append(out);
|
2021-03-29 09:11:36 +02:00
|
|
|
for (const QString &line : out.split('\n')) {
|
|
|
|
|
const QStringList parts = line.trimmed().split('\t');
|
|
|
|
|
if (parts.size() != 4) {
|
2021-07-09 14:51:22 +02:00
|
|
|
m_log->append(DockerDevice::tr("Unexpected result: %1").arg(line) + '\n');
|
2021-03-29 09:11:36 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
auto item = new DockerImageItem;
|
|
|
|
|
item->imageId = parts.at(0);
|
|
|
|
|
item->repo = parts.at(1);
|
|
|
|
|
item->tag = parts.at(2);
|
|
|
|
|
item->size = parts.at(3);
|
|
|
|
|
m_model.rootItem()->appendChild(item);
|
|
|
|
|
}
|
2021-07-09 14:51:22 +02:00
|
|
|
m_log->append(DockerDevice::tr("Done."));
|
2021-03-29 09:11:36 +02:00
|
|
|
});
|
|
|
|
|
|
2021-05-31 10:10:53 +02:00
|
|
|
connect(m_process, &Utils::QtcProcess::readyReadStandardError, this, [this] {
|
2021-07-09 14:51:22 +02:00
|
|
|
const QString out = DockerDevice::tr("Error: %1").arg(m_process->stdErr());
|
|
|
|
|
m_log->append(DockerDevice::tr("Error: %1").arg(out));
|
2021-03-29 09:11:36 +02:00
|
|
|
});
|
|
|
|
|
|
2021-05-31 10:10:53 +02:00
|
|
|
connect(m_view->selectionModel(), &QItemSelectionModel::selectionChanged, [this] {
|
2021-03-29 09:11:36 +02:00
|
|
|
const QModelIndexList selectedRows = m_view->selectionModel()->selectedRows();
|
|
|
|
|
QTC_ASSERT(selectedRows.size() == 1, return);
|
2021-05-31 10:10:53 +02:00
|
|
|
m_buttons->button(QDialogButtonBox::Ok)->setEnabled(selectedRows.size() == 1);
|
2021-03-29 09:11:36 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
m_process->start();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DockerDevice::Ptr device() const
|
|
|
|
|
{
|
|
|
|
|
const QModelIndexList selectedRows = m_view->selectionModel()->selectedRows();
|
|
|
|
|
QTC_ASSERT(selectedRows.size() == 1, return {});
|
|
|
|
|
DockerImageItem *item = m_model.itemForIndex(selectedRows.front());
|
|
|
|
|
QTC_ASSERT(item, return {});
|
|
|
|
|
|
|
|
|
|
auto device = DockerDevice::create(*item);
|
2021-06-24 09:08:11 +02:00
|
|
|
device->setupId(IDevice::ManuallyAdded, Id::fromString(item->id()));
|
2021-03-29 09:11:36 +02:00
|
|
|
device->setType(Constants::DOCKER_DEVICE_TYPE);
|
|
|
|
|
device->setMachineType(IDevice::Hardware);
|
2021-05-17 13:49:43 +02:00
|
|
|
|
2021-03-29 09:11:36 +02:00
|
|
|
return device;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
TreeModel<DockerImageItem> m_model;
|
|
|
|
|
TreeView *m_view = nullptr;
|
2021-05-31 10:10:53 +02:00
|
|
|
QTextBrowser *m_log = nullptr;
|
|
|
|
|
QDialogButtonBox *m_buttons;
|
|
|
|
|
|
2021-03-29 09:11:36 +02:00
|
|
|
QtcProcess *m_process = nullptr;
|
|
|
|
|
QString m_selectedId;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
IDevice::Ptr DockerDeviceFactory::create() const
|
|
|
|
|
{
|
|
|
|
|
DockerDeviceSetupWizard wizard;
|
|
|
|
|
if (wizard.exec() != QDialog::Accepted)
|
|
|
|
|
return IDevice::Ptr();
|
|
|
|
|
return wizard.device();
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-10 16:50:23 +02:00
|
|
|
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."));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-29 09:11:36 +02:00
|
|
|
} // Internal
|
|
|
|
|
} // Docker
|