Project: Base IDevice on AspectContainer

This is effectively the same approach as taken with ProjectConfiguration.

Having the settings separate leads to quite some boilerplate and in the
end to parallel IDevice and DeviceSettings hierarchies.

The unusual registration of the docker aspects are due to the multiple
inheritance, we need to "dynamic" downcast.

Change-Id: I50864e2009f4e525d635decf1c9beaad5e6a5f1f
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
This commit is contained in:
hjk
2024-08-01 12:30:23 +02:00
parent e78da3dfee
commit 2a7bf0fdaf
21 changed files with 229 additions and 296 deletions

View File

@@ -177,7 +177,7 @@ static IDevice::Ptr createDeviceFromInfo(const CreateAvdInfo &info)
const Id deviceId = AndroidDevice::idFromAvdInfo(info);
dev->setupId(IDevice::AutoDetected, deviceId);
dev->setMachineType(IDevice::Emulator);
dev->settings()->displayName.setValue(info.name);
dev->displayName.setValue(info.name);
dev->setDeviceState(IDevice::DeviceConnected);
dev->setAvdPath(avdFilePath() / (info.name + ".avd"));
dev->setExtraData(Constants::AndroidAvdName, info.name);
@@ -349,7 +349,7 @@ AndroidDevice::AndroidDevice()
{
setupId(IDevice::AutoDetected, Constants::ANDROID_DEVICE_ID);
setType(Constants::ANDROID_DEVICE_TYPE);
settings()->displayName.setDefaultValue(Tr::tr("Run on Android"));
displayName.setDefaultValue(Tr::tr("Run on Android"));
setDisplayType(Tr::tr("Android"));
setMachineType(IDevice::Hardware);
setOsType(OsType::OsTypeOtherUnix);
@@ -672,7 +672,7 @@ static void handleDevicesListChange(const QString &serialNumber)
} else {
AndroidDevice *newDev = new AndroidDevice();
newDev->setupId(IDevice::AutoDetected, id);
newDev->settings()->displayName.setValue(displayName);
newDev->displayName.setValue(displayName);
newDev->setMachineType(IDevice::Hardware);
newDev->setDeviceState(state);
@@ -758,7 +758,7 @@ static void handleAvdListChange(const AndroidDeviceInfoList &avdList)
AndroidDevice *newDev = new AndroidDevice;
newDev->setupId(IDevice::AutoDetected, deviceId);
newDev->settings()->displayName.setValue(displayName);
newDev->displayName.setValue(displayName);
newDev->setMachineType(item.type);
newDev->setDeviceState(item.state);

View File

@@ -26,7 +26,7 @@ ProjectExplorer::IDevice::Ptr BareMetalDeviceConfigurationWizard::device() const
{
const auto dev = BareMetalDevice::create();
dev->setupId(ProjectExplorer::IDevice::ManuallyAdded, Utils::Id());
dev->settings()->displayName.setDefaultValue(m_setupPage->configurationName());
dev->displayName.setDefaultValue(m_setupPage->configurationName());
dev->setType(Constants::BareMetalOsType);
dev->setMachineType(ProjectExplorer::IDevice::Hardware);
dev->setDebugServerProviderId(m_setupPage->debugServerProviderId());

View File

@@ -84,7 +84,7 @@ void DeviceDetector::handleDeviceEvent(QdbDeviceTracker::DeviceEventType eventTy
const QString name = Tr::tr("Boot to Qt device %1").arg(serial);
QdbDevice::Ptr device = QdbDevice::create();
device->setupId(IDevice::AutoDetected, deviceId);
device->settings()->displayName.setValue(name);
device->displayName.setValue(name);
device->setType(Qdb::Constants::QdbLinuxOsType);
device->setMachineType(IDevice::Hardware);
device->setExtraData(ProjectExplorer::Constants::SUPPORTS_RSYNC, true);

View File

@@ -207,7 +207,7 @@ public:
{
QdbDevice::Ptr device = QdbDevice::create();
device->settings()->displayName.setValue(settingsPage.deviceName());
device->displayName.setValue(settingsPage.deviceName());
device->setupId(ProjectExplorer::IDevice::ManuallyAdded, Utils::Id());
device->setType(Constants::QdbLinuxOsType);
device->setMachineType(ProjectExplorer::IDevice::Hardware);

View File

@@ -142,128 +142,8 @@ public:
}
};
void DockerDeviceSettings::fromMap(const Store &map)
{
DeviceSettings::fromMap(map);
// This is the only place where we can correctly set the default name.
// Only here do we know the image id and the repo reliably, no matter
// where or how we were created.
if (displayName.value() == displayName.defaultValue()) {
displayName.setDefaultValue(
Tr::tr("Docker Image \"%1\" (%2)").arg(repoAndTag()).arg(imageId.value()));
}
}
DockerDeviceSettings::DockerDeviceSettings()
{
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()});
mounts.setToolTip(Tr::tr("Maps paths in this list one-to-one to the docker container."));
mounts.setPlaceHolderText(Tr::tr("Host directories to mount into the container."));
extraArgs.setSettingsKey(DockerDeviceExtraArgs);
extraArgs.setLabelText(Tr::tr("Extra arguments:"));
extraArgs.setToolTip(Tr::tr("Extra arguments to pass to docker create."));
extraArgs.setDisplayStyle(StringAspect::LineEditDisplay);
clangdExecutable.setSettingsKey(DockerDeviceClangDExecutable);
clangdExecutable.setLabelText(Tr::tr("Clangd Executable:"));
clangdExecutable.setAllowPathFromDevice(true);
network.setSettingsKey("Network");
network.setLabelText(Tr::tr("Network:"));
network.setDefaultValue("bridge");
network.setFillCallback([this](const StringSelectionAspect::ResultCallback &cb) {
auto future = DockerApi::instance()->networks();
auto watcher = new QFutureWatcher<expected_str<QList<Network>>>(this);
watcher->setFuture(future);
QObject::connect(watcher,
&QFutureWatcher<expected_str<QList<Network>>>::finished,
this,
[watcher, cb]() {
expected_str<QList<Network>> result = watcher->result();
if (result) {
auto items = transform(*result, [](const Network &network) {
QStandardItem *item = new QStandardItem(network.name);
item->setData(network.name);
item->setToolTip(network.toString());
return item;
});
cb(items);
} else {
QStandardItem *errorItem = new QStandardItem(Tr::tr("Error"));
errorItem->setToolTip(result.error());
cb({errorItem});
}
});
});
connect(DockerApi::instance(),
&DockerApi::dockerDaemonAvailableChanged,
&network,
&StringSelectionAspect::refill);
clangdExecutable.setValidationFunction(
[this](const QString &newValue) -> FancyLineEdit::AsyncValidationFuture {
const FilePath rootPath = FilePath::fromParts(Constants::DOCKER_DEVICE_SCHEME,
repoAndTagEncoded(),
u"/");
return asyncRun([rootPath, newValue]() -> expected_str<QString> {
QString changedValue = newValue;
FilePath path = FilePath::fromUserInput(newValue);
if (!path.needsDevice()) {
const FilePath onDevicePath = rootPath.withNewMappedPath(path);
if (onDevicePath.exists()) {
changedValue = onDevicePath.toUserOutput();
path = onDevicePath;
} else {
return make_unexpected(
Tr::tr("The path \"%1\" does not exist.").arg(onDevicePath.toUserOutput()));
}
}
QString error;
bool result = checkClangdVersion(path, &error);
if (!result)
return make_unexpected(error);
return changedValue;
});
});
containerStatus.setText(Tr::tr("stopped"));
}
// Used for "docker run"
QString DockerDeviceSettings::repoAndTag() const
QString DockerDevice::repoAndTag() const
{
if (repo() == "<none>")
return imageId();
@@ -274,12 +154,12 @@ QString DockerDeviceSettings::repoAndTag() const
return repo() + ':' + tag();
}
QString DockerDeviceSettings::repoAndTagEncoded() const
QString DockerDevice::repoAndTagEncoded() const
{
return repoAndTag().replace(':', '.');
}
FilePath DockerDeviceSettings::rootPath() const
FilePath DockerDevice::rootPath() const
{
return FilePath::fromParts(Constants::DOCKER_DEVICE_SCHEME, repoAndTagEncoded(), u"/");
}
@@ -289,9 +169,8 @@ class DockerDevicePrivate : public QObject
public:
DockerDevicePrivate(DockerDevice *parent)
: q(parent)
, deviceSettings(static_cast<DockerDeviceSettings *>(q->settings()))
{
QObject::connect(deviceSettings, &DockerDeviceSettings::applied, this, [this] {
QObject::connect(q, &DockerDevice::applied, this, [this] {
if (!m_container.isEmpty()) {
stopCurrentContainer();
}
@@ -310,10 +189,6 @@ public:
QString containerId() { return m_container; }
QString repoAndTag() const { return deviceSettings->repoAndTag(); }
QString repoAndTagEncoded() const { return deviceSettings->repoAndTagEncoded(); }
QString dockerImageId() const { return deviceSettings->imageId(); }
expected_str<QPair<Utils::OsType, Utils::OsArch>> osTypeAndArch() const;
expected_str<Environment> environment();
@@ -337,11 +212,11 @@ public:
std::optional<FilePath> clangdExecutable() const
{
if (deviceSettings->clangdExecutable().isEmpty())
if (q->clangdExecutableAspect().isEmpty())
return std::nullopt;
if (!deviceSettings->clangdExecutable().needsDevice())
return deviceSettings->rootPath().withNewMappedPath(deviceSettings->clangdExecutable());
return deviceSettings->clangdExecutable();
if (!q->clangdExecutableAspect().needsDevice())
return q->rootPath().withNewMappedPath(q->clangdExecutableAspect());
return q->clangdExecutableAspect();
}
QStringList createMountArgs() const;
@@ -349,7 +224,6 @@ public:
bool isImageAvailable() const;
DockerDevice *const q;
DockerDeviceSettings *deviceSettings;
struct MountPair
{
@@ -569,7 +443,7 @@ Tasks DockerDevicePrivate::validateMounts() const
{
Tasks result;
for (const FilePath &mount : deviceSettings->mounts()) {
for (const FilePath &mount : q->mounts()) {
if (!mount.isDir()) {
const QString message = Tr::tr("Path \"%1\" is not a directory or does not exist.")
.arg(mount.toUserOutput());
@@ -593,10 +467,114 @@ QString DockerDeviceFileAccess::mapToDevicePath(const QString &hostPath) const
return newPath;
}
DockerDevice::DockerDevice(std::unique_ptr<DockerDeviceSettings> deviceSettings)
: ProjectExplorer::IDevice(std::move(deviceSettings))
, d(new DockerDevicePrivate(this))
DockerDevice::DockerDevice()
: d(new DockerDevicePrivate(this))
{
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()});
mounts.setToolTip(Tr::tr("Maps paths in this list one-to-one to the docker container."));
mounts.setPlaceHolderText(Tr::tr("Host directories to mount into the container."));
extraArgs.setSettingsKey(DockerDeviceExtraArgs);
extraArgs.setLabelText(Tr::tr("Extra arguments:"));
extraArgs.setToolTip(Tr::tr("Extra arguments to pass to docker create."));
extraArgs.setDisplayStyle(StringAspect::LineEditDisplay);
clangdExecutableAspect.setSettingsKey(DockerDeviceClangDExecutable);
clangdExecutableAspect.setLabelText(Tr::tr("Clangd Executable:"));
clangdExecutableAspect.setAllowPathFromDevice(true);
network.setSettingsKey("Network");
network.setLabelText(Tr::tr("Network:"));
network.setDefaultValue("bridge");
network.setFillCallback([this](const StringSelectionAspect::ResultCallback &cb) {
auto future = DockerApi::instance()->networks();
auto watcher = new QFutureWatcher<expected_str<QList<Network>>>(this);
watcher->setFuture(future);
QObject::connect(watcher,
&QFutureWatcher<expected_str<QList<Network>>>::finished,
this,
[watcher, cb]() {
expected_str<QList<Network>> result = watcher->result();
if (result) {
auto items = transform(*result, [](const Network &network) {
QStandardItem *item = new QStandardItem(network.name);
item->setData(network.name);
item->setToolTip(network.toString());
return item;
});
cb(items);
} else {
QStandardItem *errorItem = new QStandardItem(Tr::tr("Error"));
errorItem->setToolTip(result.error());
cb({errorItem});
}
});
});
connect(DockerApi::instance(),
&DockerApi::dockerDaemonAvailableChanged,
&network,
&StringSelectionAspect::refill);
clangdExecutableAspect.setValidationFunction(
[this](const QString &newValue) -> FancyLineEdit::AsyncValidationFuture {
const FilePath rootPath = FilePath::fromParts(Constants::DOCKER_DEVICE_SCHEME,
repoAndTagEncoded(),
u"/");
return asyncRun([rootPath, newValue]() -> expected_str<QString> {
QString changedValue = newValue;
FilePath path = FilePath::fromUserInput(newValue);
if (!path.needsDevice()) {
const FilePath onDevicePath = rootPath.withNewMappedPath(path);
if (onDevicePath.exists()) {
changedValue = onDevicePath.toUserOutput();
path = onDevicePath;
} else {
return make_unexpected(
Tr::tr("The path \"%1\" does not exist.").arg(onDevicePath.toUserOutput()));
}
}
QString error;
bool result = checkClangdVersion(path, &error);
if (!result)
return make_unexpected(error);
return changedValue;
});
});
containerStatus.setText(Tr::tr("stopped"));
auto createBridgeFileAccess = [this]() -> expected_str<std::unique_ptr<DeviceFileAccess>> {
expected_str<FilePath> cmdBridgePath = d->getCmdBridgePath();
@@ -874,7 +852,7 @@ QStringList DockerDevicePrivate::createMountArgs() const
QStringList cmds;
QList<MountPair> mounts;
for (const FilePath &m : deviceSettings->mounts())
for (const FilePath &m : q->mounts())
mounts.append({m, m});
if (cmdBridgePath && cmdBridgePath->isSameDevice(settings().dockerBinaryPath()))
@@ -893,12 +871,12 @@ bool DockerDevicePrivate::isImageAvailable() const
Process proc;
proc.setCommand(
{settings().dockerBinaryPath(),
{"image", "list", deviceSettings->repoAndTag(), "--format", "{{.Repository}}:{{.Tag}}"}});
{"image", "list", q->repoAndTag(), "--format", "{{.Repository}}:{{.Tag}}"}});
proc.runBlocking();
if (proc.result() != ProcessResult::FinishedWithSuccess)
return false;
if (proc.stdOut().trimmed() == deviceSettings->repoAndTag())
if (proc.stdOut().trimmed() == q->repoAndTag())
return true;
return false;
@@ -919,26 +897,26 @@ CommandLine DockerDevicePrivate::createCommandLine()
#ifdef Q_OS_UNIX
// no getuid() and getgid() on Windows.
if (deviceSettings->useLocalUidGid())
if (q->useLocalUidGid())
dockerCreate.addArgs({"-u", QString("%1:%2").arg(getuid()).arg(getgid())});
#endif
if (!deviceSettings->network().isEmpty()) {
if (!q->network().isEmpty()) {
dockerCreate.addArg("--network");
dockerCreate.addArg(deviceSettings->network());
dockerCreate.addArg(q->network());
}
dockerCreate.addArgs(createMountArgs());
if (!deviceSettings->keepEntryPoint())
if (!q->keepEntryPoint())
dockerCreate.addArgs({"--entrypoint", "/bin/sh"});
if (deviceSettings->enableLldbFlags())
if (q->enableLldbFlags())
dockerCreate.addArgs({"--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined"});
dockerCreate.addArgs(deviceSettings->extraArgs(), CommandLine::Raw);
dockerCreate.addArgs(q->extraArgs(), CommandLine::Raw);
dockerCreate.addArg(deviceSettings->repoAndTag());
dockerCreate.addArg(q->repoAndTag());
return dockerCreate;
}
@@ -946,7 +924,7 @@ CommandLine DockerDevicePrivate::createCommandLine()
expected_str<QString> DockerDevicePrivate::createContainer()
{
if (!isImageAvailable())
return make_unexpected(Tr::tr("Image \"%1\" is not available.").arg(repoAndTag()));
return make_unexpected(Tr::tr("Image \"%1\" is not available.").arg(q->repoAndTag()));
const CommandLine cmdLine = createCommandLine();
@@ -1005,7 +983,7 @@ expected_str<void> DockerDevicePrivate::updateContainerAccess()
result = make_unexpected(QString("Failed to start container: %1").arg(result.error()));
QTimer::singleShot(0, this, [this, containerStatus] {
deviceSettings->containerStatus.setText(containerStatus);
q->containerStatus.setText(containerStatus);
});
return result;
@@ -1019,13 +997,19 @@ void DockerDevice::setMounts(const QStringList &mounts) const
void DockerDevice::fromMap(const Store &map)
{
ProjectExplorer::IDevice::fromMap(map);
d->deviceSettings->fromMap(map);
// This is the only place where we can correctly set the default name.
// Only here do we know the image id and the repo reliably, no matter
// where or how we were created.
if (displayName.value() == displayName.defaultValue()) {
displayName.setDefaultValue(
Tr::tr("Docker Image \"%1\" (%2)").arg(repoAndTag()).arg(imageId.value()));
}
}
void DockerDevice::toMap(Store &map) const
{
IDevice::toMap(map);
d->deviceSettings->toMap(map);
}
ProcessInterface *DockerDevice::createProcessInterface() const
@@ -1046,15 +1030,10 @@ bool DockerDevice::usableAsBuildDevice() const
FilePath DockerDevice::filePath(const QString &pathOnDevice) const
{
return FilePath::fromParts(Constants::DOCKER_DEVICE_SCHEME,
d->repoAndTagEncoded(),
repoAndTagEncoded(),
pathOnDevice);
}
FilePath DockerDevice::rootPath() const
{
return d->deviceSettings->rootPath();
}
bool DockerDevice::handlesFile(const FilePath &filePath) const
{
if (filePath.scheme() == u"device" && filePath.host() == id().toString())
@@ -1062,13 +1041,13 @@ bool DockerDevice::handlesFile(const FilePath &filePath) const
const bool isDockerScheme = filePath.scheme() == Constants::DOCKER_DEVICE_SCHEME;
if (isDockerScheme && filePath.host() == d->dockerImageId())
if (isDockerScheme && filePath.host() == imageId())
return true;
if (isDockerScheme && filePath.host() == d->repoAndTagEncoded())
if (isDockerScheme && filePath.host() == repoAndTagEncoded())
return true;
if (isDockerScheme && filePath.host() == d->repoAndTag())
if (isDockerScheme && filePath.host() == repoAndTag())
return true;
return false;
@@ -1301,12 +1280,10 @@ public:
m_proxyModel->mapToSource(selectedRows.front()));
QTC_ASSERT(item, return {});
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));
auto device = DockerDevice::create();
device->repo.setValue(item->repo);
device->tag.setValue(item->tag);
device->imageId.setValue(item->imageId);
return device;
}
@@ -1336,7 +1313,7 @@ DockerDeviceFactory::DockerDeviceFactory()
return wizard.device();
});
setConstructionFunction([this] {
auto device = DockerDevice::create(std::make_unique<DockerDeviceSettings>());
auto device = DockerDevice::create();
QMutexLocker lk(&m_deviceListMutex);
m_existingDevices.push_back(device);
return device;
@@ -1357,7 +1334,7 @@ expected_str<QPair<Utils::OsType, Utils::OsArch>> DockerDevicePrivate::osTypeAnd
Process proc;
proc.setCommand(
{settings().dockerBinaryPath(),
{"image", "inspect", repoAndTag(), "--format", "{{.Os}}\t{{.Architecture}}"}});
{"image", "inspect", q->repoAndTag(), "--format", "{{.Os}}\t{{.Architecture}}"}});
proc.runBlocking();
if (proc.result() != ProcessResult::FinishedWithSuccess)
return make_unexpected(Tr::tr("Failed to inspect image: %1").arg(proc.allOutput()));
@@ -1398,8 +1375,8 @@ void DockerDevicePrivate::shutdown()
void DockerDevicePrivate::changeMounts(QStringList newMounts)
{
newMounts.removeDuplicates();
if (deviceSettings->mounts.value() != newMounts) {
deviceSettings->mounts.value() = newMounts;
if (q->mounts.value() != newMounts) {
q->mounts.value() = newMounts;
stopCurrentContainer(); // Force re-start with new mounts.
}
}
@@ -1407,7 +1384,7 @@ void DockerDevicePrivate::changeMounts(QStringList newMounts)
expected_str<FilePath> DockerDevicePrivate::localSource(const FilePath &other) const
{
const auto devicePath = FilePath::fromString(other.path());
for (const FilePath &mount : deviceSettings->mounts()) {
for (const FilePath &mount : q->mounts()) {
const FilePath mountPoint = mount;
if (devicePath.isChildOf(mountPoint)) {
const FilePath relativePath = devicePath.relativeChildPath(mountPoint);
@@ -1423,7 +1400,7 @@ bool DockerDevicePrivate::ensureReachable(const FilePath &other)
if (other.isSameDevice(q->rootPath()))
return true;
for (const FilePath &mount : deviceSettings->mounts()) {
for (const FilePath &mount : q->mounts()) {
if (other.isChildOf(mount))
return true;

View File

@@ -12,46 +12,18 @@
namespace Docker::Internal {
class DockerDeviceSettings : public ProjectExplorer::DeviceSettings
{
public:
DockerDeviceSettings();
void fromMap(const Utils::Store &map) override;
QString repoAndTag() const;
QString repoAndTagEncoded() const;
Utils::FilePath rootPath() const;
Utils::StringAspect imageId{this};
Utils::StringAspect repo{this};
Utils::StringAspect tag{this};
Utils::BoolAspect useLocalUidGid{this};
Utils::FilePathListAspect mounts{this};
Utils::BoolAspect keepEntryPoint{this};
Utils::BoolAspect enableLldbFlags{this};
Utils::FilePathAspect clangdExecutable{this};
Utils::StringSelectionAspect network{this};
Utils::StringAspect extraArgs{this};
Utils::TextDisplay containerStatus{this};
};
class DockerDevice : public ProjectExplorer::IDevice
{
public:
using Ptr = std::shared_ptr<DockerDevice>;
using ConstPtr = std::shared_ptr<const DockerDevice>;
explicit DockerDevice(std::unique_ptr<DockerDeviceSettings> settings);
DockerDevice();
~DockerDevice();
void shutdown();
static Ptr create(std::unique_ptr<DockerDeviceSettings> settings)
{
return Ptr(new DockerDevice(std::move(settings)));
}
static Ptr create() { return Ptr(new DockerDevice); }
Utils::CommandLine createCommandLine() const;
@@ -85,6 +57,22 @@ public:
bool prepareForBuild(const ProjectExplorer::Target *target) override;
std::optional<Utils::FilePath> clangdExecutable() const override;
QString repoAndTag() const;
QString repoAndTagEncoded() const;
Utils::StringAspect imageId{this};
Utils::StringAspect repo{this};
Utils::StringAspect tag{this};
Utils::BoolAspect useLocalUidGid{this};
Utils::FilePathListAspect mounts{this};
Utils::BoolAspect keepEntryPoint{this};
Utils::BoolAspect enableLldbFlags{this};
Utils::FilePathAspect clangdExecutableAspect{this};
Utils::StringSelectionAspect network{this};
Utils::StringAspect extraArgs{this};
Utils::TextDisplay containerStatus{this};
protected:
void fromMap(const Utils::Store &map) final;
void toMap(Utils::Store &map) const final;

View File

@@ -34,8 +34,6 @@ DockerDeviceWidget::DockerDeviceWidget(const IDevice::Ptr &device)
auto dockerDevice = std::dynamic_pointer_cast<DockerDevice>(device);
QTC_ASSERT(dockerDevice, return);
DockerDeviceSettings *deviceSettings = static_cast<DockerDeviceSettings *>(device->settings());
using namespace Layouting;
auto daemonStateLabel = new QLabel(Tr::tr("Daemon state:"));
@@ -58,13 +56,13 @@ DockerDeviceWidget::DockerDeviceWidget(const IDevice::Ptr &device)
auto pathListLabel = new InfoLabel(Tr::tr("Paths to mount:"));
pathListLabel->setAdditionalToolTip(Tr::tr("Source directory list should not be empty."));
auto markupMounts = [deviceSettings, pathListLabel] {
const bool isEmpty = deviceSettings->mounts.volatileValue().isEmpty();
auto markupMounts = [dockerDevice, pathListLabel] {
const bool isEmpty = dockerDevice->mounts.volatileValue().isEmpty();
pathListLabel->setType(isEmpty ? InfoLabel::Warning : InfoLabel::None);
};
markupMounts();
connect(&deviceSettings->mounts, &FilePathListAspect::volatileValueChanged, this, markupMounts);
connect(&dockerDevice->mounts, &FilePathListAspect::volatileValueChanged, this, markupMounts);
auto logView = new QTextBrowser;
connect(&m_kitItemDetector, &KitDetector::logOutput,
@@ -104,7 +102,7 @@ DockerDeviceWidget::DockerDeviceWidget(const IDevice::Ptr &device)
connect(autoDetectButton,
&QPushButton::clicked,
this,
[this, logView, dockerDevice, searchPaths, deviceSettings] {
[this, logView, dockerDevice, searchPaths] {
logView->clear();
expected_str<void> startResult = dockerDevice->updateContainerAccess();
@@ -121,7 +119,7 @@ DockerDeviceWidget::DockerDeviceWidget(const IDevice::Ptr &device)
});
if (!clangdPath.isEmpty())
deviceSettings->clangdExecutable.setValue(clangdPath);
dockerDevice->clangdExecutableAspect.setValue(clangdPath);
m_kitItemDetector.autoDetect(dockerDevice->id().toString(), searchPaths());
@@ -169,20 +167,20 @@ DockerDeviceWidget::DockerDeviceWidget(const IDevice::Ptr &device)
Column {
noMargin,
Form {
deviceSettings->repo, br,
deviceSettings->tag, br,
deviceSettings->imageId, br,
dockerDevice->repo, br,
dockerDevice->tag, br,
dockerDevice->imageId, br,
daemonStateLabel, m_daemonReset, m_daemonState, br,
Tr::tr("Container state:"), deviceSettings->containerStatus, br,
deviceSettings->useLocalUidGid, br,
deviceSettings->keepEntryPoint, br,
deviceSettings->enableLldbFlags, br,
deviceSettings->clangdExecutable, br,
deviceSettings->network, br,
deviceSettings->extraArgs, br,
Tr::tr("Container state:"), dockerDevice->containerStatus, br,
dockerDevice->useLocalUidGid, br,
dockerDevice->keepEntryPoint, br,
dockerDevice->enableLldbFlags, br,
dockerDevice->clangdExecutableAspect, br,
dockerDevice->network, br,
dockerDevice->extraArgs, br,
Column {
pathListLabel,
deviceSettings->mounts,
dockerDevice->mounts,
}, br,
If { dockerDevice->isAutoDetected(), {}, {detectionControls} },
noMargin,
@@ -199,7 +197,7 @@ DockerDeviceWidget::DockerDeviceWidget(const IDevice::Ptr &device)
};
QObject::connect(searchDirsComboBox, &QComboBox::activated, this, updateDirectoriesLineEdit);
connect(deviceSettings, &AspectContainer::applied, this, [createLineLabel, dockerDevice] {
connect(&*dockerDevice, &AspectContainer::applied, this, [createLineLabel, dockerDevice] {
createLineLabel->setText(dockerDevice->createCommandLine().toUserOutput());
});
}

View File

@@ -107,7 +107,7 @@ IosDevice::IosDevice(CtorHelper)
: m_lastPort(Constants::IOS_DEVICE_PORT_START)
{
setType(Constants::IOS_DEVICE_TYPE);
settings()->displayName.setDefaultValue(IosDevice::name());
displayName.setDefaultValue(IosDevice::name());
setDisplayType(Tr::tr("iOS"));
setMachineType(IDevice::Hardware);
setOsType(Utils::OsTypeMac);
@@ -248,7 +248,7 @@ void IosDeviceManager::deviceConnected(const QString &uid, const QString &name)
if (!dev) {
auto newDev = new IosDevice(uid);
if (!name.isNull())
newDev->settings()->displayName.setValue(name);
newDev->displayName.setValue(name);
qCDebug(detectLog) << "adding ios device " << uid;
devManager->addDevice(IDevice::ConstPtr(newDev));
} else if (dev->deviceState() != IDevice::DeviceConnected &&
@@ -364,7 +364,7 @@ void IosDeviceManager::deviceInfo(const QString &uid,
}
if (!skipUpdate) {
if (info.contains(kDeviceName))
newDev->settings()->displayName.setValue(info.value(kDeviceName));
newDev->displayName.setValue(info.value(kDeviceName));
newDev->m_extraInfo = info;
newDev->m_handler = handler;
qCDebug(detectLog) << "updated info of ios device " << uid;

View File

@@ -28,7 +28,7 @@ IosSimulator::IosSimulator(Id id)
setType(Constants::IOS_SIMULATOR_TYPE);
setMachineType(IDevice::Emulator);
setOsType(Utils::OsTypeMac);
settings()->displayName.setDefaultValue(Tr::tr("iOS Simulator"));
displayName.setDefaultValue(Tr::tr("iOS Simulator"));
setDisplayType(Tr::tr("iOS Simulator"));
setDeviceState(DeviceStateUnknown);
}

View File

@@ -16,7 +16,7 @@ McuSupportDevice::McuSupportDevice()
setupId(IDevice::AutoDetected, Constants::DEVICE_ID);
setType(Constants::DEVICE_TYPE);
const QString displayNameAndType = Tr::tr("MCU Device");
settings()->displayName.setDefaultValue(displayNameAndType);
displayName.setDefaultValue(displayNameAndType);
setDisplayType(displayNameAndType);
setDeviceState(IDevice::DeviceStateUnknown);
setMachineType(IDevice::Hardware);

View File

@@ -42,7 +42,7 @@ DesktopDevice::DesktopDevice()
setupId(IDevice::AutoDetected, DESKTOP_DEVICE_ID);
setType(DESKTOP_DEVICE_TYPE);
settings()->displayName.setDefaultValue(Tr::tr("Local PC"));
displayName.setDefaultValue(Tr::tr("Local PC"));
setDisplayType(Tr::tr("Desktop"));
setDeviceState(IDevice::DeviceStateUnknown);

View File

@@ -261,8 +261,7 @@ void DeviceManager::addDevice(const IDevice::ConstPtr &_device)
}
// TODO: make it thread safe?
device->settings()->displayName.setValue(
Utils::makeUniquelyNumbered(device->displayName(), names));
device->displayName.setValue(Utils::makeUniquelyNumbered(device->displayName(), names));
const int pos = d->indexForId(device->id());
@@ -568,7 +567,7 @@ void ProjectExplorerTest::testDeviceManager()
TestDeviceFactory factory;
TestDevice::Ptr dev = IDevice::Ptr(new TestDevice);
dev->settings()->displayName.setValue(QLatin1String("blubbdiblubbfurz!"));
dev->displayName.setValue(QLatin1String("blubbdiblubbfurz!"));
QVERIFY(dev->isAutoDetected());
QCOMPARE(dev->deviceState(), IDevice::DeviceStateUnknown);
QCOMPARE(dev->type(), TestDevice::testTypeId());
@@ -629,7 +628,7 @@ void ProjectExplorerTest::testDeviceManager()
TestDevice::Ptr dev3 = IDevice::Ptr(new TestDevice);
QVERIFY(dev->id() != dev3->id());
dev3->settings()->displayName.setValue(dev->displayName());
dev3->displayName.setValue(dev->displayName());
mgr->addDevice(dev3);
QCOMPARE(mgr->deviceAt(mgr->deviceCount() - 1)->displayName(),
QString(dev3->displayName() + QLatin1Char('2')));

View File

@@ -274,7 +274,7 @@ void DeviceSettingsWidget::setDeviceInfoWidgetsEnabled(bool enable)
void DeviceSettingsWidget::updateDeviceFromUi()
{
currentDevice()->settings()->apply();
currentDevice()->doApply();
if (m_configWidget)
m_configWidget->updateDeviceFromUi();
}
@@ -340,7 +340,7 @@ void DeviceSettingsWidget::currentDeviceChanged(int index)
}
Layouting::Column item{Layouting::noMargin};
device->settings()->displayName.addToLayout(item);
device->displayName.addToLayout(item);
QWidget *newEdit = item.emerge();
QLayoutItem *oldItem = m_generalFormLayout->replaceWidget(m_deviceNameEditWidget, newEdit);
QTC_CHECK(oldItem);

View File

@@ -120,16 +120,10 @@ const IDevice::MachineType DefaultMachineType = IDevice::Hardware;
const int DefaultTimeout = 10;
namespace Internal {
class IDevicePrivate
{
public:
IDevicePrivate(std::unique_ptr<DeviceSettings> s)
: settings(std::move(s))
{
if (!settings)
settings = std::make_unique<DeviceSettings>();
}
QString displayType;
Id type;
IDevice::Origin origin = IDevice::AutoDetected;
@@ -153,12 +147,14 @@ public:
QList<IDevice::DeviceAction> deviceActions;
Store extraData;
IDevice::OpenTerminal openTerminal;
std::unique_ptr<DeviceSettings> settings;
};
} // namespace Internal
DeviceSettings::DeviceSettings()
DeviceTester::DeviceTester(QObject *parent) : QObject(parent) { }
IDevice::IDevice()
: d(new Internal::IDevicePrivate)
{
setAutoApply(false);
@@ -201,13 +197,6 @@ DeviceSettings::DeviceSettings()
});
}
DeviceTester::DeviceTester(QObject *parent) : QObject(parent) { }
IDevice::IDevice(std::unique_ptr<DeviceSettings> settings)
: d(new Internal::IDevicePrivate(std::move(settings)))
{
}
IDevice::~IDevice() = default;
void IDevice::setOpenTerminal(const IDevice::OpenTerminal &openTerminal)
@@ -319,11 +308,6 @@ expected_str<Environment> IDevice::systemEnvironmentWithError() const
return access->deviceEnvironment();
}
QString IDevice::displayName() const
{
return d->settings->displayName();
}
QString IDevice::displayType() const
{
return d->displayType;
@@ -495,8 +479,8 @@ Id IDevice::idFromMap(const Store &map)
void IDevice::fromMap(const Store &map)
{
AspectContainer::fromMap(map);
d->type = typeFromMap(map);
settings()->fromMap(map);
d->id = Id::fromSetting(map.value(IdKey));
d->osType = osTypeFromString(map.value(ClientOsTypeKey).toString()).value_or(OsTypeLinux);
@@ -543,7 +527,7 @@ void IDevice::fromMap(const Store &map)
void IDevice::toMap(Store &map) const
{
d->settings->toMap(map);
AspectContainer::toMap(map);
map.insert(TypeKey, d->type.toString());
map.insert(ClientOsTypeKey, osTypeToString(d->osType));
@@ -587,11 +571,6 @@ IDevice::Ptr IDevice::clone() const
return device;
}
DeviceSettings *IDevice::settings() const
{
return d->settings.get();
}
QString IDevice::deviceStateToString() const
{
switch (d->deviceState) {
@@ -725,6 +704,11 @@ std::optional<Utils::FilePath> IDevice::clangdExecutable() const
return std::nullopt;
}
void IDevice::doApply() const
{
const_cast<IDevice *>(this)->apply();
}
void DeviceProcessSignalOperation::setDebuggerCommand(const FilePath &cmd)
{
m_debuggerCommand = cmd;

View File

@@ -83,16 +83,9 @@ public:
std::function<QList<Utils::Port>(const QByteArray &commandOutput)> parsePorts;
};
class PROJECTEXPLORER_EXPORT DeviceSettings : public Utils::AspectContainer
{
public:
DeviceSettings();
Utils::StringAspect displayName{this};
};
// See cpp file for documentation.
class PROJECTEXPLORER_EXPORT IDevice : public std::enable_shared_from_this<IDevice>
class PROJECTEXPLORER_EXPORT IDevice
: public Utils::AspectContainer, public std::enable_shared_from_this<IDevice>
{
friend class Internal::IDevicePrivate;
public:
@@ -107,9 +100,7 @@ public:
virtual Ptr clone() const;
DeviceSettings *settings() const;
QString displayName() const;
Utils::StringAspect displayName{this};
// Provide some information on the device suitable for formated
// output, e.g. in tool tips. Get a list of name value pairs.
@@ -229,8 +220,10 @@ public:
virtual void checkOsType() {}
void doApply() const;
protected:
IDevice(std::unique_ptr<DeviceSettings> settings = nullptr);
IDevice();
virtual void fromMap(const Utils::Store &map);
virtual void toMap(Utils::Store &map) const;

View File

@@ -79,7 +79,7 @@ IDevice::Ptr IDeviceFactory::construct() const
IDevice::Ptr device = m_constructor();
QTC_ASSERT(device, return {});
device->settings()->displayName.setDefaultValue(displayName());
device->displayName.setDefaultValue(displayName());
return device;
}

View File

@@ -1313,7 +1313,7 @@ void BuildDeviceKitAspectFactory::addToMacroExpander(Kit *kit, MacroExpander *ex
});
expander->registerVariable("BuildDevice:Name", Tr::tr("Build device name"), [kit] {
const IDevice::ConstPtr device = BuildDeviceKitAspect::device(kit);
return device ? device->settings()->displayName() : QString();
return device ? device->displayName() : QString();
});
expander
->registerFileVariables("BuildDevice::Root", Tr::tr("Build device root directory"), [kit] {

View File

@@ -64,7 +64,7 @@ public:
QnxDevice()
{
setDisplayType(Tr::tr("QNX"));
settings()->displayName.setDefaultValue(Tr::tr("QNX Device"));
displayName.setDefaultValue(Tr::tr("QNX Device"));
setOsType(OsTypeOtherUnix);
setupId(IDevice::ManuallyAdded);
setType(Constants::QNX_QNX_OS_TYPE);

View File

@@ -307,12 +307,6 @@ public:
LinuxDevicePrivate *m_dev;
};
class LinuxDeviceSettings : public DeviceSettings
{
public:
LinuxDeviceSettings() { displayName.setDefaultValue(Tr::tr("Remote Linux Device")); }
};
class LinuxDevicePrivate
{
public:
@@ -1019,12 +1013,12 @@ private:
// LinuxDevice
LinuxDevice::LinuxDevice()
: IDevice(std::make_unique<LinuxDeviceSettings>())
, d(new LinuxDevicePrivate(this))
: d(new LinuxDevicePrivate(this))
{
setFileAccess(&d->m_fileAccess);
setDisplayType(Tr::tr("Remote Linux"));
setOsType(OsTypeLinux);
displayName.setDefaultValue(Tr::tr("Remote Linux Device"));
setupId(IDevice::ManuallyAdded, Utils::Id());
setType(Constants::GenericLinuxOsType);

View File

@@ -77,7 +77,7 @@ private:
&& !m_userNameLineEdit->text().trimmed().isEmpty();
}
bool validatePage() final {
m_device->settings()->displayName.setValue(m_nameLineEdit->text().trimmed());
m_device->displayName.setValue(m_nameLineEdit->text().trimmed());
SshParameters sshParams = m_device->sshParameters();
sshParams.setHost(m_hostNameLineEdit->text().trimmed());
sshParams.setUserName(m_userNameLineEdit->text().trimmed());

View File

@@ -35,7 +35,7 @@ public:
setupId(IDevice::AutoDetected, Constants::WEBASSEMBLY_DEVICE_DEVICE_ID);
setType(Constants::WEBASSEMBLY_DEVICE_TYPE);
const QString displayNameAndType = Tr::tr("Web Browser");
settings()->displayName.setDefaultValue(displayNameAndType);
displayName.setDefaultValue(displayNameAndType);
setDisplayType(displayNameAndType);
setDeviceState(IDevice::DeviceStateUnknown);
setMachineType(IDevice::Hardware);