forked from qt-creator/qt-creator
Docker: Switch to aspect settings
Change-Id: Id04c48caf2ddd33e176ee424273690b80c77232b Reviewed-by: <github-actions-qt-creator@cristianadam.eu> Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
@@ -32,7 +32,9 @@
|
|||||||
#include <qtsupport/qtversionmanager.h>
|
#include <qtsupport/qtversionmanager.h>
|
||||||
|
|
||||||
#include <utils/algorithm.h>
|
#include <utils/algorithm.h>
|
||||||
|
#include <utils/async.h>
|
||||||
#include <utils/basetreeview.h>
|
#include <utils/basetreeview.h>
|
||||||
|
#include <utils/clangutils.h>
|
||||||
#include <utils/devicefileaccess.h>
|
#include <utils/devicefileaccess.h>
|
||||||
#include <utils/deviceshell.h>
|
#include <utils/deviceshell.h>
|
||||||
#include <utils/environment.h>
|
#include <utils/environment.h>
|
||||||
@@ -87,6 +89,15 @@ Q_LOGGING_CATEGORY(dockerDeviceLog, "qtc.docker.device", QtWarningMsg);
|
|||||||
|
|
||||||
namespace Docker::Internal {
|
namespace Docker::Internal {
|
||||||
|
|
||||||
|
const char DockerDeviceDataImageIdKey[] = "DockerDeviceDataImageId";
|
||||||
|
const char DockerDeviceDataRepoKey[] = "DockerDeviceDataRepo";
|
||||||
|
const char DockerDeviceDataTagKey[] = "DockerDeviceDataTag";
|
||||||
|
const char DockerDeviceUseOutsideUser[] = "DockerDeviceUseUidGid";
|
||||||
|
const char DockerDeviceMappedPaths[] = "DockerDeviceMappedPaths";
|
||||||
|
const char DockerDeviceKeepEntryPoint[] = "DockerDeviceKeepEntryPoint";
|
||||||
|
const char DockerDeviceEnableLldbFlags[] = "DockerDeviceEnableLldbFlags";
|
||||||
|
const char DockerDeviceClangDExecutable[] = "DockerDeviceClangDExecutable";
|
||||||
|
|
||||||
class ContainerShell : public Utils::DeviceShell
|
class ContainerShell : public Utils::DeviceShell
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -128,19 +139,87 @@ public:
|
|||||||
DockerDevicePrivate *m_dev = nullptr;
|
DockerDevicePrivate *m_dev = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DockerDeviceSettings : public DeviceSettings
|
DockerDeviceSettings::DockerDeviceSettings()
|
||||||
{
|
{
|
||||||
public:
|
displayName.setDefaultValue(Tr::tr("Docker Image"));
|
||||||
DockerDeviceSettings() { displayName.setDefaultValue(Tr::tr("Docker Image")); }
|
|
||||||
};
|
imageId.setSettingsKey(DockerDeviceDataImageIdKey);
|
||||||
|
imageId.setLabelText(Tr::tr("Image ID:"));
|
||||||
|
imageId.setReadOnly(true);
|
||||||
|
|
||||||
|
repo.setSettingsKey(DockerDeviceDataRepoKey);
|
||||||
|
repo.setLabelText(Tr::tr("Repository:"));
|
||||||
|
repo.setReadOnly(true);
|
||||||
|
|
||||||
|
tag.setSettingsKey(DockerDeviceDataTagKey);
|
||||||
|
tag.setLabelText(Tr::tr("Tag:"));
|
||||||
|
tag.setReadOnly(true);
|
||||||
|
|
||||||
|
useLocalUidGid.setSettingsKey(DockerDeviceUseOutsideUser);
|
||||||
|
useLocalUidGid.setLabelText(Tr::tr("Run as outside user:"));
|
||||||
|
useLocalUidGid.setDefaultValue(true);
|
||||||
|
useLocalUidGid.setLabelPlacement(BoolAspect::LabelPlacement::InExtraLabel);
|
||||||
|
|
||||||
|
keepEntryPoint.setSettingsKey(DockerDeviceKeepEntryPoint);
|
||||||
|
keepEntryPoint.setLabelText(Tr::tr("Do not modify entry point:"));
|
||||||
|
keepEntryPoint.setDefaultValue(false);
|
||||||
|
keepEntryPoint.setLabelPlacement(BoolAspect::LabelPlacement::InExtraLabel);
|
||||||
|
|
||||||
|
enableLldbFlags.setSettingsKey(DockerDeviceEnableLldbFlags);
|
||||||
|
enableLldbFlags.setLabelText(Tr::tr("Enable flags needed for LLDB:"));
|
||||||
|
enableLldbFlags.setDefaultValue(false);
|
||||||
|
enableLldbFlags.setLabelPlacement(BoolAspect::LabelPlacement::InExtraLabel);
|
||||||
|
|
||||||
|
mounts.setSettingsKey(DockerDeviceMappedPaths);
|
||||||
|
mounts.setLabelText(Tr::tr("Paths to mount:"));
|
||||||
|
mounts.setDefaultValue({Core::DocumentManager::projectsDirectory().toString()});
|
||||||
|
|
||||||
|
clangdExecutable.setSettingsKey(DockerDeviceClangDExecutable);
|
||||||
|
clangdExecutable.setLabelText(Tr::tr("Clangd Executable:"));
|
||||||
|
clangdExecutable.setAllowPathFromDevice(true);
|
||||||
|
|
||||||
|
clangdExecutable.setValidationFunction(
|
||||||
|
[](const QString &newValue) -> FancyLineEdit::AsyncValidationFuture {
|
||||||
|
return Utils::asyncRun([newValue]() -> expected_str<QString> {
|
||||||
|
QString error;
|
||||||
|
bool result = Utils::checkClangdVersion(FilePath::fromUserInput(newValue), &error);
|
||||||
|
if (!result)
|
||||||
|
return make_unexpected(error);
|
||||||
|
return newValue;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used for "docker run"
|
||||||
|
QString DockerDeviceSettings::repoAndTag() const
|
||||||
|
{
|
||||||
|
if (repo() == "<none>")
|
||||||
|
return imageId();
|
||||||
|
|
||||||
|
if (tag() == "<none>")
|
||||||
|
return repo();
|
||||||
|
|
||||||
|
return repo() + ':' + tag();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString DockerDeviceSettings::repoAndTagEncoded() const
|
||||||
|
{
|
||||||
|
return repoAndTag().replace(':', '.');
|
||||||
|
}
|
||||||
|
|
||||||
class DockerDevicePrivate : public QObject
|
class DockerDevicePrivate : public QObject
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
DockerDevicePrivate(DockerDevice *parent, DockerDeviceData data)
|
DockerDevicePrivate(DockerDevice *parent)
|
||||||
: q(parent)
|
: q(parent)
|
||||||
, m_data(std::move(data))
|
, deviceSettings(static_cast<DockerDeviceSettings *>(q->settings()))
|
||||||
{}
|
{
|
||||||
|
QObject::connect(deviceSettings, &DockerDeviceSettings::applied, this, [this] {
|
||||||
|
if (!m_container.isEmpty()) {
|
||||||
|
stopCurrentContainer();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
~DockerDevicePrivate() { stopCurrentContainer(); }
|
~DockerDevicePrivate() { stopCurrentContainer(); }
|
||||||
|
|
||||||
@@ -153,12 +232,10 @@ public:
|
|||||||
expected_str<FilePath> localSource(const FilePath &other) const;
|
expected_str<FilePath> localSource(const FilePath &other) const;
|
||||||
|
|
||||||
QString containerId() { return m_container; }
|
QString containerId() { return m_container; }
|
||||||
DockerDeviceData data() { return m_data; }
|
|
||||||
void setData(const DockerDeviceData &data);
|
|
||||||
|
|
||||||
QString repoAndTag() const { return m_data.repoAndTag(); }
|
QString repoAndTag() const { return deviceSettings->repoAndTag(); }
|
||||||
QString repoAndTagEncoded() const { return m_data.repoAndTagEncoded(); }
|
QString repoAndTagEncoded() const { return deviceSettings->repoAndTagEncoded(); }
|
||||||
QString dockerImageId() const { return m_data.imageId; }
|
QString dockerImageId() const { return deviceSettings->imageId(); }
|
||||||
|
|
||||||
Environment environment();
|
Environment environment();
|
||||||
|
|
||||||
@@ -179,9 +256,9 @@ public:
|
|||||||
|
|
||||||
std::optional<FilePath> clangdExecutable() const
|
std::optional<FilePath> clangdExecutable() const
|
||||||
{
|
{
|
||||||
if (m_data.clangdExecutable.isEmpty())
|
if (deviceSettings->clangdExecutable().isEmpty())
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
return m_data.clangdExecutable;
|
return deviceSettings->clangdExecutable();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool addTemporaryMount(const FilePath &path, const FilePath &containerPath);
|
bool addTemporaryMount(const FilePath &path, const FilePath &containerPath);
|
||||||
@@ -191,7 +268,7 @@ public:
|
|||||||
bool isImageAvailable() const;
|
bool isImageAvailable() const;
|
||||||
|
|
||||||
DockerDevice *const q;
|
DockerDevice *const q;
|
||||||
DockerDeviceData m_data;
|
DockerDeviceSettings *deviceSettings;
|
||||||
|
|
||||||
struct TemporaryMountInfo
|
struct TemporaryMountInfo
|
||||||
{
|
{
|
||||||
@@ -383,7 +460,7 @@ Tasks DockerDevicePrivate::validateMounts() const
|
|||||||
{
|
{
|
||||||
Tasks result;
|
Tasks result;
|
||||||
|
|
||||||
for (const QString &mount : m_data.mounts) {
|
for (const QString &mount : deviceSettings->mounts()) {
|
||||||
const FilePath path = FilePath::fromUserInput(mount);
|
const FilePath path = FilePath::fromUserInput(mount);
|
||||||
if (!path.isDir()) {
|
if (!path.isDir()) {
|
||||||
const QString message = Tr::tr("Path \"%1\" is not a directory or does not exist.")
|
const QString message = Tr::tr("Path \"%1\" is not a directory or does not exist.")
|
||||||
@@ -415,9 +492,9 @@ QString DockerDeviceFileAccess::mapToDevicePath(const QString &hostPath) const
|
|||||||
return newPath;
|
return newPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
DockerDevice::DockerDevice(const DockerDeviceData &data)
|
DockerDevice::DockerDevice(std::unique_ptr<DockerDeviceSettings> deviceSettings)
|
||||||
: ProjectExplorer::IDevice(std::make_unique<DockerDeviceSettings>())
|
: ProjectExplorer::IDevice(std::move(deviceSettings))
|
||||||
, d(new DockerDevicePrivate(this, data))
|
, d(new DockerDevicePrivate(this))
|
||||||
{
|
{
|
||||||
setFileAccess(&d->m_fileAccess);
|
setFileAccess(&d->m_fileAccess);
|
||||||
setDisplayType(Tr::tr("Docker"));
|
setDisplayType(Tr::tr("Docker"));
|
||||||
@@ -425,8 +502,9 @@ DockerDevice::DockerDevice(const DockerDeviceData &data)
|
|||||||
setupId(IDevice::ManuallyAdded);
|
setupId(IDevice::ManuallyAdded);
|
||||||
setType(Constants::DOCKER_DEVICE_TYPE);
|
setType(Constants::DOCKER_DEVICE_TYPE);
|
||||||
setMachineType(IDevice::Hardware);
|
setMachineType(IDevice::Hardware);
|
||||||
settings()->displayName.setDefaultValue(
|
d->deviceSettings->displayName.setDefaultValue(Tr::tr("Docker Image \"%1\" (%2)")
|
||||||
Tr::tr("Docker Image \"%1\" (%2)").arg(data.repoAndTag()).arg(data.imageId));
|
.arg(d->deviceSettings->repoAndTag())
|
||||||
|
.arg(d->deviceSettings->imageId()));
|
||||||
setAllowEmptyCommand(true);
|
setAllowEmptyCommand(true);
|
||||||
|
|
||||||
setOpenTerminal([this](const Environment &env, const FilePath &workingDir) {
|
setOpenTerminal([this](const Environment &env, const FilePath &workingDir) {
|
||||||
@@ -467,21 +545,6 @@ void DockerDevice::shutdown()
|
|||||||
d->shutdown();
|
d->shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
const DockerDeviceData DockerDevice::data() const
|
|
||||||
{
|
|
||||||
return d->data();
|
|
||||||
}
|
|
||||||
|
|
||||||
DockerDeviceData DockerDevice::data()
|
|
||||||
{
|
|
||||||
return d->data();
|
|
||||||
}
|
|
||||||
|
|
||||||
void DockerDevice::setData(const DockerDeviceData &data)
|
|
||||||
{
|
|
||||||
d->setData(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DockerDevice::updateContainerAccess() const
|
bool DockerDevice::updateContainerAccess() const
|
||||||
{
|
{
|
||||||
return d->updateContainerAccess();
|
return d->updateContainerAccess();
|
||||||
@@ -642,7 +705,7 @@ QStringList DockerDevicePrivate::createMountArgs() const
|
|||||||
{
|
{
|
||||||
QStringList cmds;
|
QStringList cmds;
|
||||||
QList<TemporaryMountInfo> mounts = m_temporaryMounts;
|
QList<TemporaryMountInfo> mounts = m_temporaryMounts;
|
||||||
for (const QString &m : m_data.mounts)
|
for (const QString &m : deviceSettings->mounts())
|
||||||
mounts.append({FilePath::fromUserInput(m), FilePath::fromUserInput(m)});
|
mounts.append({FilePath::fromUserInput(m), FilePath::fromUserInput(m)});
|
||||||
|
|
||||||
for (const TemporaryMountInfo &mi : mounts) {
|
for (const TemporaryMountInfo &mi : mounts) {
|
||||||
@@ -658,12 +721,12 @@ bool DockerDevicePrivate::isImageAvailable() const
|
|||||||
Process proc;
|
Process proc;
|
||||||
proc.setCommand(
|
proc.setCommand(
|
||||||
{settings().dockerBinaryPath(),
|
{settings().dockerBinaryPath(),
|
||||||
{"image", "list", m_data.repoAndTag(), "--format", "{{.Repository}}:{{.Tag}}"}});
|
{"image", "list", deviceSettings->repoAndTag(), "--format", "{{.Repository}}:{{.Tag}}"}});
|
||||||
proc.runBlocking();
|
proc.runBlocking();
|
||||||
if (proc.result() != ProcessResult::FinishedWithSuccess)
|
if (proc.result() != ProcessResult::FinishedWithSuccess)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (proc.stdOut().trimmed() == m_data.repoAndTag())
|
if (proc.stdOut().trimmed() == deviceSettings->repoAndTag())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -689,19 +752,19 @@ bool DockerDevicePrivate::createContainer()
|
|||||||
|
|
||||||
#ifdef Q_OS_UNIX
|
#ifdef Q_OS_UNIX
|
||||||
// no getuid() and getgid() on Windows.
|
// no getuid() and getgid() on Windows.
|
||||||
if (m_data.useLocalUidGid)
|
if (deviceSettings->useLocalUidGid())
|
||||||
dockerCreate.addArgs({"-u", QString("%1:%2").arg(getuid()).arg(getgid())});
|
dockerCreate.addArgs({"-u", QString("%1:%2").arg(getuid()).arg(getgid())});
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
dockerCreate.addArgs(createMountArgs());
|
dockerCreate.addArgs(createMountArgs());
|
||||||
|
|
||||||
if (!m_data.keepEntryPoint)
|
if (!deviceSettings->keepEntryPoint())
|
||||||
dockerCreate.addArgs({"--entrypoint", "/bin/sh"});
|
dockerCreate.addArgs({"--entrypoint", "/bin/sh"});
|
||||||
|
|
||||||
if (m_data.enableLldbFlags)
|
if (deviceSettings->enableLldbFlags())
|
||||||
dockerCreate.addArgs({"--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined"});
|
dockerCreate.addArgs({"--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined"});
|
||||||
|
|
||||||
dockerCreate.addArg(m_data.repoAndTag());
|
dockerCreate.addArg(deviceSettings->repoAndTag());
|
||||||
|
|
||||||
qCDebug(dockerDeviceLog).noquote() << "RUNNING: " << dockerCreate.toUserOutput();
|
qCDebug(dockerDeviceLog).noquote() << "RUNNING: " << dockerCreate.toUserOutput();
|
||||||
Process createProcess;
|
Process createProcess;
|
||||||
@@ -775,47 +838,16 @@ void DockerDevice::setMounts(const QStringList &mounts) const
|
|||||||
d->changeMounts(mounts);
|
d->changeMounts(mounts);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char DockerDeviceDataImageIdKey[] = "DockerDeviceDataImageId";
|
|
||||||
const char DockerDeviceDataRepoKey[] = "DockerDeviceDataRepo";
|
|
||||||
const char DockerDeviceDataTagKey[] = "DockerDeviceDataTag";
|
|
||||||
const char DockerDeviceDataSizeKey[] = "DockerDeviceDataSize";
|
|
||||||
const char DockerDeviceUseOutsideUser[] = "DockerDeviceUseUidGid";
|
|
||||||
const char DockerDeviceMappedPaths[] = "DockerDeviceMappedPaths";
|
|
||||||
const char DockerDeviceKeepEntryPoint[] = "DockerDeviceKeepEntryPoint";
|
|
||||||
const char DockerDeviceEnableLldbFlags[] = "DockerDeviceEnableLldbFlags";
|
|
||||||
const char DockerDeviceClangDExecutable[] = "DockerDeviceClangDExecutable";
|
|
||||||
|
|
||||||
void DockerDevice::fromMap(const Store &map)
|
void DockerDevice::fromMap(const Store &map)
|
||||||
{
|
{
|
||||||
ProjectExplorer::IDevice::fromMap(map);
|
ProjectExplorer::IDevice::fromMap(map);
|
||||||
DockerDeviceData data;
|
d->deviceSettings->fromMap(map);
|
||||||
|
|
||||||
data.repo = map.value(DockerDeviceDataRepoKey).toString();
|
|
||||||
data.tag = map.value(DockerDeviceDataTagKey).toString();
|
|
||||||
data.imageId = map.value(DockerDeviceDataImageIdKey).toString();
|
|
||||||
data.size = map.value(DockerDeviceDataSizeKey).toString();
|
|
||||||
data.useLocalUidGid = map.value(DockerDeviceUseOutsideUser, HostOsInfo::isLinuxHost()).toBool();
|
|
||||||
data.mounts = map.value(DockerDeviceMappedPaths).toStringList();
|
|
||||||
data.keepEntryPoint = map.value(DockerDeviceKeepEntryPoint).toBool();
|
|
||||||
data.enableLldbFlags = map.value(DockerDeviceEnableLldbFlags).toBool();
|
|
||||||
data.clangdExecutable = FilePath::fromSettings(map.value(DockerDeviceClangDExecutable));
|
|
||||||
d->setData(data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Store DockerDevice::toMap() const
|
Store DockerDevice::toMap() const
|
||||||
{
|
{
|
||||||
Store map = ProjectExplorer::IDevice::toMap();
|
Store map = ProjectExplorer::IDevice::toMap();
|
||||||
DockerDeviceData data = d->data();
|
d->deviceSettings->toMap(map);
|
||||||
|
|
||||||
map.insert(DockerDeviceDataRepoKey, data.repo);
|
|
||||||
map.insert(DockerDeviceDataTagKey, data.tag);
|
|
||||||
map.insert(DockerDeviceDataImageIdKey, data.imageId);
|
|
||||||
map.insert(DockerDeviceDataSizeKey, data.size);
|
|
||||||
map.insert(DockerDeviceUseOutsideUser, data.useLocalUidGid);
|
|
||||||
map.insert(DockerDeviceMappedPaths, data.mounts);
|
|
||||||
map.insert(DockerDeviceKeepEntryPoint, data.keepEntryPoint);
|
|
||||||
map.insert(DockerDeviceEnableLldbFlags, data.enableLldbFlags);
|
|
||||||
map.insert(DockerDeviceClangDExecutable, data.clangdExecutable.toSettings());
|
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -839,14 +871,6 @@ FilePath DockerDevice::filePath(const QString &pathOnDevice) const
|
|||||||
return FilePath::fromParts(Constants::DOCKER_DEVICE_SCHEME,
|
return FilePath::fromParts(Constants::DOCKER_DEVICE_SCHEME,
|
||||||
d->repoAndTagEncoded(),
|
d->repoAndTagEncoded(),
|
||||||
pathOnDevice);
|
pathOnDevice);
|
||||||
|
|
||||||
// The following would work, but gives no hint on repo and tag
|
|
||||||
// result.setScheme("docker");
|
|
||||||
// result.setHost(d->m_data.imageId);
|
|
||||||
|
|
||||||
// The following would work, but gives no hint on repo, tag and imageid
|
|
||||||
// result.setScheme("device");
|
|
||||||
// result.setHost(id().toString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Utils::FilePath DockerDevice::rootPath() const
|
Utils::FilePath DockerDevice::rootPath() const
|
||||||
@@ -940,7 +964,7 @@ RunResult DockerDevicePrivate::runInShell(const CommandLine &cmd, const QByteArr
|
|||||||
|
|
||||||
// Factory
|
// Factory
|
||||||
|
|
||||||
class DockerImageItem final : public TreeItem, public DockerDeviceData
|
class DockerImageItem final : public TreeItem
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
DockerImageItem() {}
|
DockerImageItem() {}
|
||||||
@@ -968,6 +992,11 @@ public:
|
|||||||
|
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString repo;
|
||||||
|
QString tag;
|
||||||
|
QString imageId;
|
||||||
|
QString size;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DockerDeviceSetupWizard final : public QDialog
|
class DockerDeviceSetupWizard final : public QDialog
|
||||||
@@ -1099,7 +1128,12 @@ public:
|
|||||||
m_proxyModel->mapToSource(selectedRows.front()));
|
m_proxyModel->mapToSource(selectedRows.front()));
|
||||||
QTC_ASSERT(item, return {});
|
QTC_ASSERT(item, return {});
|
||||||
|
|
||||||
auto device = DockerDevice::create(*item);
|
auto deviceSettings = std::make_unique<DockerDeviceSettings>();
|
||||||
|
deviceSettings->repo.setValue(item->repo);
|
||||||
|
deviceSettings->tag.setValue(item->tag);
|
||||||
|
deviceSettings->imageId.setValue(item->imageId);
|
||||||
|
|
||||||
|
auto device = DockerDevice::create(std::move(deviceSettings));
|
||||||
|
|
||||||
return device;
|
return device;
|
||||||
}
|
}
|
||||||
@@ -1129,7 +1163,7 @@ DockerDeviceFactory::DockerDeviceFactory()
|
|||||||
return wizard.device();
|
return wizard.device();
|
||||||
});
|
});
|
||||||
setConstructionFunction([this] {
|
setConstructionFunction([this] {
|
||||||
auto device = DockerDevice::create({});
|
auto device = DockerDevice::create(std::make_unique<DockerDeviceSettings>());
|
||||||
QMutexLocker lk(&m_deviceListMutex);
|
QMutexLocker lk(&m_deviceListMutex);
|
||||||
m_existingDevices.push_back(device);
|
m_existingDevices.push_back(device);
|
||||||
return device;
|
return device;
|
||||||
@@ -1155,7 +1189,7 @@ bool DockerDevicePrivate::addTemporaryMount(const FilePath &path, const FilePath
|
|||||||
if (alreadyAdded)
|
if (alreadyAdded)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const bool alreadyManuallyAdded = anyOf(m_data.mounts, [path](const QString &mount) {
|
const bool alreadyManuallyAdded = anyOf(deviceSettings->mounts(), [path](const QString &mount) {
|
||||||
return mount == path.path();
|
return mount == path.path();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1191,8 +1225,8 @@ void DockerDevicePrivate::shutdown()
|
|||||||
void DockerDevicePrivate::changeMounts(QStringList newMounts)
|
void DockerDevicePrivate::changeMounts(QStringList newMounts)
|
||||||
{
|
{
|
||||||
newMounts.removeDuplicates();
|
newMounts.removeDuplicates();
|
||||||
if (m_data.mounts != newMounts) {
|
if (deviceSettings->mounts() != newMounts) {
|
||||||
m_data.mounts = newMounts;
|
deviceSettings->mounts() = newMounts;
|
||||||
stopCurrentContainer(); // Force re-start with new mounts.
|
stopCurrentContainer(); // Force re-start with new mounts.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1207,7 +1241,7 @@ expected_str<FilePath> DockerDevicePrivate::localSource(const FilePath &other) c
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const QString &mount : m_data.mounts) {
|
for (const QString &mount : deviceSettings->mounts()) {
|
||||||
const FilePath mountPoint = FilePath::fromString(mount);
|
const FilePath mountPoint = FilePath::fromString(mount);
|
||||||
if (devicePath.isChildOf(mountPoint)) {
|
if (devicePath.isChildOf(mountPoint)) {
|
||||||
const FilePath relativePath = devicePath.relativeChildPath(mountPoint);
|
const FilePath relativePath = devicePath.relativeChildPath(mountPoint);
|
||||||
@@ -1223,7 +1257,7 @@ bool DockerDevicePrivate::ensureReachable(const FilePath &other)
|
|||||||
if (other.isSameDevice(q->rootPath()))
|
if (other.isSameDevice(q->rootPath()))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
for (const QString &mount : m_data.mounts) {
|
for (const QString &mount : deviceSettings->mounts()) {
|
||||||
const FilePath fMount = FilePath::fromString(mount);
|
const FilePath fMount = FilePath::fromString(mount);
|
||||||
if (other.isChildOf(fMount))
|
if (other.isChildOf(fMount))
|
||||||
return true;
|
return true;
|
||||||
@@ -1250,18 +1284,6 @@ bool DockerDevicePrivate::ensureReachable(const FilePath &other)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DockerDevicePrivate::setData(const DockerDeviceData &data)
|
|
||||||
{
|
|
||||||
if (m_data != data) {
|
|
||||||
m_data = data;
|
|
||||||
|
|
||||||
// Force restart if the container is already running
|
|
||||||
if (!m_container.isEmpty()) {
|
|
||||||
stopCurrentContainer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DockerDevice::prepareForBuild(const Target *target)
|
bool DockerDevice::prepareForBuild(const Target *target)
|
||||||
{
|
{
|
||||||
return d->prepareForBuild(target);
|
return d->prepareForBuild(target);
|
||||||
|
@@ -14,42 +14,22 @@
|
|||||||
|
|
||||||
namespace Docker::Internal {
|
namespace Docker::Internal {
|
||||||
|
|
||||||
class DockerDeviceData
|
class DockerDeviceSettings : public ProjectExplorer::DeviceSettings
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
bool operator==(const DockerDeviceData &other) const
|
DockerDeviceSettings();
|
||||||
{
|
|
||||||
return imageId == other.imageId && repo == other.repo && tag == other.tag
|
|
||||||
&& useLocalUidGid == other.useLocalUidGid && mounts == other.mounts
|
|
||||||
&& keepEntryPoint == other.keepEntryPoint && enableLldbFlags == other.enableLldbFlags
|
|
||||||
&& clangdExecutable == other.clangdExecutable;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator!=(const DockerDeviceData &other) const { return !(*this == other); }
|
QString repoAndTag() const;
|
||||||
|
QString repoAndTagEncoded() const;
|
||||||
|
|
||||||
// Used for "docker run"
|
Utils::StringAspect imageId{this};
|
||||||
QString repoAndTag() const
|
Utils::StringAspect repo{this};
|
||||||
{
|
Utils::StringAspect tag{this};
|
||||||
if (repo == "<none>")
|
Utils::BoolAspect useLocalUidGid{this};
|
||||||
return imageId;
|
Utils::StringListAspect mounts{this};
|
||||||
|
Utils::BoolAspect keepEntryPoint{this};
|
||||||
if (tag == "<none>")
|
Utils::BoolAspect enableLldbFlags{this};
|
||||||
return repo;
|
Utils::FilePathAspect clangdExecutable{this};
|
||||||
|
|
||||||
return repo + ':' + tag;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString repoAndTagEncoded() const { return repoAndTag().replace(':', '.'); }
|
|
||||||
|
|
||||||
QString imageId;
|
|
||||||
QString repo;
|
|
||||||
QString tag;
|
|
||||||
QString size;
|
|
||||||
bool useLocalUidGid = true;
|
|
||||||
QStringList mounts = {Core::DocumentManager::projectsDirectory().toString()};
|
|
||||||
bool keepEntryPoint = false;
|
|
||||||
bool enableLldbFlags = false;
|
|
||||||
Utils::FilePath clangdExecutable;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class DockerDevice : public ProjectExplorer::IDevice
|
class DockerDevice : public ProjectExplorer::IDevice
|
||||||
@@ -58,14 +38,14 @@ public:
|
|||||||
using Ptr = QSharedPointer<DockerDevice>;
|
using Ptr = QSharedPointer<DockerDevice>;
|
||||||
using ConstPtr = QSharedPointer<const DockerDevice>;
|
using ConstPtr = QSharedPointer<const DockerDevice>;
|
||||||
|
|
||||||
explicit DockerDevice(const DockerDeviceData &data);
|
explicit DockerDevice(std::unique_ptr<DockerDeviceSettings> settings);
|
||||||
~DockerDevice();
|
~DockerDevice();
|
||||||
|
|
||||||
void shutdown();
|
void shutdown();
|
||||||
|
|
||||||
static Ptr create(const DockerDeviceData &data)
|
static Ptr create(std::unique_ptr<DockerDeviceSettings> settings)
|
||||||
{
|
{
|
||||||
return Ptr(new DockerDevice(data));
|
return Ptr(new DockerDevice(std::move(settings)));
|
||||||
}
|
}
|
||||||
|
|
||||||
ProjectExplorer::IDeviceWidget *createWidget() override;
|
ProjectExplorer::IDeviceWidget *createWidget() override;
|
||||||
@@ -87,11 +67,6 @@ public:
|
|||||||
|
|
||||||
Utils::Environment systemEnvironment() const override;
|
Utils::Environment systemEnvironment() const override;
|
||||||
|
|
||||||
const DockerDeviceData data() const;
|
|
||||||
DockerDeviceData data();
|
|
||||||
|
|
||||||
void setData(const DockerDeviceData &data);
|
|
||||||
|
|
||||||
bool updateContainerAccess() const;
|
bool updateContainerAccess() const;
|
||||||
void setMounts(const QStringList &mounts) const;
|
void setMounts(const QStringList &mounts) const;
|
||||||
|
|
||||||
@@ -124,5 +99,3 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Docker::Internal
|
} // namespace Docker::Internal
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(Docker::Internal::DockerDeviceData)
|
|
||||||
|
@@ -33,22 +33,9 @@ DockerDeviceWidget::DockerDeviceWidget(const IDevice::Ptr &device)
|
|||||||
auto dockerDevice = device.dynamicCast<DockerDevice>();
|
auto dockerDevice = device.dynamicCast<DockerDevice>();
|
||||||
QTC_ASSERT(dockerDevice, return);
|
QTC_ASSERT(dockerDevice, return);
|
||||||
|
|
||||||
m_data = dockerDevice->data();
|
DockerDeviceSettings *deviceSettings = static_cast<DockerDeviceSettings *>(device->settings());
|
||||||
|
|
||||||
auto repoLabel = new QLabel(Tr::tr("Repository:"));
|
using namespace Layouting;
|
||||||
m_repoLineEdit = new QLineEdit;
|
|
||||||
m_repoLineEdit->setText(m_data.repo);
|
|
||||||
m_repoLineEdit->setEnabled(false);
|
|
||||||
|
|
||||||
auto tagLabel = new QLabel(Tr::tr("Tag:"));
|
|
||||||
m_tagLineEdit = new QLineEdit;
|
|
||||||
m_tagLineEdit->setText(m_data.tag);
|
|
||||||
m_tagLineEdit->setEnabled(false);
|
|
||||||
|
|
||||||
auto idLabel = new QLabel(Tr::tr("Image ID:"));
|
|
||||||
m_idLineEdit = new QLineEdit;
|
|
||||||
m_idLineEdit->setText(m_data.imageId);
|
|
||||||
m_idLineEdit->setEnabled(false);
|
|
||||||
|
|
||||||
auto daemonStateLabel = new QLabel(Tr::tr("Daemon state:"));
|
auto daemonStateLabel = new QLabel(Tr::tr("Daemon state:"));
|
||||||
m_daemonReset = new QToolButton;
|
m_daemonReset = new QToolButton;
|
||||||
@@ -67,58 +54,6 @@ DockerDeviceWidget::DockerDeviceWidget(const IDevice::Ptr &device)
|
|||||||
DockerApi::recheckDockerDaemon();
|
DockerApi::recheckDockerDaemon();
|
||||||
});
|
});
|
||||||
|
|
||||||
m_keepEntryPoint = new QCheckBox(Tr::tr("Do not modify entry point"));
|
|
||||||
m_keepEntryPoint->setToolTip(
|
|
||||||
Tr::tr("Prevents modifying the entry point of the image. Enable only if "
|
|
||||||
"the image starts into a shell."));
|
|
||||||
m_keepEntryPoint->setChecked(m_data.keepEntryPoint);
|
|
||||||
m_keepEntryPoint->setEnabled(true);
|
|
||||||
|
|
||||||
connect(m_keepEntryPoint, &QCheckBox::toggled, this, [this, dockerDevice](bool on) {
|
|
||||||
m_data.keepEntryPoint = on;
|
|
||||||
dockerDevice->setData(m_data);
|
|
||||||
});
|
|
||||||
|
|
||||||
m_enableLldbFlags = new QCheckBox(Tr::tr("Enable flags needed for LLDB"));
|
|
||||||
m_enableLldbFlags->setToolTip(Tr::tr("Adds the following flags to the container "
|
|
||||||
"to allow LLDB to run: "
|
|
||||||
"--cap-add=SYS_PTRACE --security-opt seccomp=unconfined"));
|
|
||||||
m_enableLldbFlags->setChecked(m_data.enableLldbFlags);
|
|
||||||
m_enableLldbFlags->setEnabled(true);
|
|
||||||
|
|
||||||
connect(m_enableLldbFlags, &QCheckBox::toggled, this, [this, dockerDevice](bool on) {
|
|
||||||
m_data.enableLldbFlags = on;
|
|
||||||
dockerDevice->setData(m_data);
|
|
||||||
});
|
|
||||||
|
|
||||||
m_runAsOutsideUser = new QCheckBox(Tr::tr("Run as outside user"));
|
|
||||||
m_runAsOutsideUser->setToolTip(Tr::tr("Uses user ID and group ID of the user running Qt Creator "
|
|
||||||
"in the docker container."));
|
|
||||||
m_runAsOutsideUser->setChecked(m_data.useLocalUidGid);
|
|
||||||
m_runAsOutsideUser->setEnabled(HostOsInfo::isAnyUnixHost());
|
|
||||||
|
|
||||||
connect(m_runAsOutsideUser, &QCheckBox::toggled, this, [this, dockerDevice](bool on) {
|
|
||||||
m_data.useLocalUidGid = on;
|
|
||||||
dockerDevice->setData(m_data);
|
|
||||||
});
|
|
||||||
|
|
||||||
auto clangDLabel = new QLabel(Tr::tr("Clangd Executable:"));
|
|
||||||
|
|
||||||
m_clangdExecutable = new PathChooser();
|
|
||||||
m_clangdExecutable->setExpectedKind(PathChooser::ExistingCommand);
|
|
||||||
m_clangdExecutable->setHistoryCompleter("Docker.ClangdExecutable.History");
|
|
||||||
m_clangdExecutable->setAllowPathFromDevice(true);
|
|
||||||
m_clangdExecutable->setFilePath(m_data.clangdExecutable);
|
|
||||||
m_clangdExecutable->setValidationFunction(
|
|
||||||
[chooser = m_clangdExecutable](FancyLineEdit *, QString *error) {
|
|
||||||
return Utils::checkClangdVersion(chooser->filePath(), error);
|
|
||||||
});
|
|
||||||
|
|
||||||
connect(m_clangdExecutable, &PathChooser::rawPathChanged, this, [this, dockerDevice] {
|
|
||||||
m_data.clangdExecutable = m_clangdExecutable->filePath();
|
|
||||||
dockerDevice->setData(m_data);
|
|
||||||
});
|
|
||||||
|
|
||||||
auto pathListLabel = new InfoLabel(Tr::tr("Paths to mount:"));
|
auto pathListLabel = new InfoLabel(Tr::tr("Paths to mount:"));
|
||||||
pathListLabel->setAdditionalToolTip(Tr::tr("Source directory list should not be empty."));
|
pathListLabel->setAdditionalToolTip(Tr::tr("Source directory list should not be empty."));
|
||||||
|
|
||||||
@@ -126,7 +61,7 @@ DockerDeviceWidget::DockerDeviceWidget(const IDevice::Ptr &device)
|
|||||||
m_pathsListEdit->setPlaceholderText(Tr::tr("Host directories to mount into the container"));
|
m_pathsListEdit->setPlaceholderText(Tr::tr("Host directories to mount into the container"));
|
||||||
m_pathsListEdit->setToolTip(Tr::tr("Maps paths in this list one-to-one to the "
|
m_pathsListEdit->setToolTip(Tr::tr("Maps paths in this list one-to-one to the "
|
||||||
"docker container."));
|
"docker container."));
|
||||||
m_pathsListEdit->setPathList(m_data.mounts);
|
m_pathsListEdit->setPathList(deviceSettings->mounts());
|
||||||
m_pathsListEdit->setMaximumHeight(100);
|
m_pathsListEdit->setMaximumHeight(100);
|
||||||
m_pathsListEdit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
m_pathsListEdit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||||
|
|
||||||
@@ -136,9 +71,12 @@ DockerDeviceWidget::DockerDeviceWidget(const IDevice::Ptr &device)
|
|||||||
};
|
};
|
||||||
markupMounts();
|
markupMounts();
|
||||||
|
|
||||||
connect(m_pathsListEdit, &PathListEditor::changed, this, [this, dockerDevice, markupMounts] {
|
connect(m_pathsListEdit,
|
||||||
m_data.mounts = m_pathsListEdit->pathList();
|
&PathListEditor::changed,
|
||||||
dockerDevice->setData(m_data);
|
this,
|
||||||
|
[this, dockerDevice, markupMounts, deviceSettings] {
|
||||||
|
deviceSettings->mounts.setVolatileValue(m_pathsListEdit->pathList());
|
||||||
|
// dockerDevice->setData(m_data);
|
||||||
markupMounts();
|
markupMounts();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -177,20 +115,21 @@ DockerDeviceWidget::DockerDeviceWidget(const IDevice::Ptr &device)
|
|||||||
return paths;
|
return paths;
|
||||||
};
|
};
|
||||||
|
|
||||||
connect(autoDetectButton, &QPushButton::clicked, this,
|
connect(autoDetectButton,
|
||||||
[this, logView, dockerDevice, searchPaths] {
|
&QPushButton::clicked,
|
||||||
|
this,
|
||||||
|
[this, logView, dockerDevice, searchPaths, deviceSettings] {
|
||||||
logView->clear();
|
logView->clear();
|
||||||
dockerDevice->updateContainerAccess();
|
dockerDevice->updateContainerAccess();
|
||||||
|
|
||||||
const FilePath clangdPath = dockerDevice->filePath("clangd")
|
const FilePath clangdPath
|
||||||
.searchInPath({},
|
= dockerDevice->filePath("clangd")
|
||||||
FilePath::AppendToPath,
|
.searchInPath({}, FilePath::AppendToPath, [](const FilePath &clangd) {
|
||||||
[](const FilePath &clangd) {
|
|
||||||
return Utils::checkClangdVersion(clangd);
|
return Utils::checkClangdVersion(clangd);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!clangdPath.isEmpty())
|
if (!clangdPath.isEmpty())
|
||||||
m_clangdExecutable->setFilePath(clangdPath);
|
deviceSettings->clangdExecutable.setValue(clangdPath);
|
||||||
|
|
||||||
m_kitItemDetector.autoDetect(dockerDevice->id().toString(), searchPaths());
|
m_kitItemDetector.autoDetect(dockerDevice->id().toString(), searchPaths());
|
||||||
|
|
||||||
@@ -234,19 +173,19 @@ DockerDeviceWidget::DockerDeviceWidget(const IDevice::Ptr &device)
|
|||||||
};
|
};
|
||||||
|
|
||||||
Form {
|
Form {
|
||||||
repoLabel, m_repoLineEdit, br,
|
deviceSettings->repo, br,
|
||||||
tagLabel, m_tagLineEdit, br,
|
deviceSettings->tag, br,
|
||||||
idLabel, m_idLineEdit, br,
|
deviceSettings->imageId, br,
|
||||||
daemonStateLabel, m_daemonReset, m_daemonState, br,
|
daemonStateLabel, m_daemonReset, m_daemonState, br,
|
||||||
m_runAsOutsideUser, br,
|
deviceSettings->useLocalUidGid, br,
|
||||||
m_keepEntryPoint, br,
|
deviceSettings->keepEntryPoint, br,
|
||||||
m_enableLldbFlags, br,
|
deviceSettings->enableLldbFlags, br,
|
||||||
clangDLabel, m_clangdExecutable, br,
|
deviceSettings->clangdExecutable, br,
|
||||||
Column {
|
Column {
|
||||||
pathListLabel,
|
pathListLabel,
|
||||||
m_pathsListEdit,
|
m_pathsListEdit,
|
||||||
}, br,
|
}, br,
|
||||||
(dockerDevice->isAutoDetected() ? Column {} : std::move(detectionControls)),
|
If { dockerDevice->isAutoDetected(), {}, {detectionControls} },
|
||||||
noMargin,
|
noMargin,
|
||||||
}.attachTo(this);
|
}.attachTo(this);
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
@@ -31,20 +31,11 @@ public:
|
|||||||
void updateDaemonStateTexts();
|
void updateDaemonStateTexts();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QLineEdit *m_repoLineEdit;
|
|
||||||
QLineEdit *m_tagLineEdit;
|
|
||||||
QLineEdit *m_idLineEdit;
|
|
||||||
QToolButton *m_daemonReset;
|
|
||||||
QLabel *m_daemonState;
|
|
||||||
QCheckBox *m_runAsOutsideUser;
|
|
||||||
QCheckBox *m_keepEntryPoint;
|
|
||||||
QCheckBox *m_enableLldbFlags;
|
|
||||||
Utils::PathChooser *m_clangdExecutable;
|
|
||||||
|
|
||||||
Utils::PathListEditor *m_pathsListEdit;
|
Utils::PathListEditor *m_pathsListEdit;
|
||||||
KitDetector m_kitItemDetector;
|
QLabel *m_daemonState;
|
||||||
|
QToolButton *m_daemonReset;
|
||||||
|
|
||||||
DockerDeviceData m_data;
|
KitDetector m_kitItemDetector;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // Docker::Internal
|
} // Docker::Internal
|
||||||
|
Reference in New Issue
Block a user