ProjectExplorer: Change IDevice to use Aspects

Change-Id: I1619426fcbaf5ca909abe7d57d4289de407c2830
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Marcus Tillmanns
2025-04-23 10:48:51 +02:00
parent ea06a8568e
commit f3f56edbad
16 changed files with 372 additions and 410 deletions

View File

@@ -141,7 +141,7 @@ void QdbDevice::setupDefaultNetworkSettings(const QString &host)
parameters.setPort(22); parameters.setPort(22);
parameters.setTimeout(10); parameters.setTimeout(10);
parameters.setAuthenticationType(SshParameters::AuthenticationTypeAll); parameters.setAuthenticationType(SshParameters::AuthenticationTypeAll);
setSshParameters(parameters); setDefaultSshParameters(parameters);
} }
// QdbDeviceWizard // QdbDeviceWizard

View File

@@ -380,7 +380,7 @@ ExecutableItem debugServerRecipe(const Storage<DebuggerData> &storage, const Sin
const auto port = runControl->debugChannel().port(); const auto port = runControl->debugChannel().port();
cmd.addArg(QString(":%1").arg(port)); cmd.addArg(QString(":%1").arg(port));
if (runControl->device()->extraData(ProjectExplorer::Constants::SSH_FORWARD_DEBUGSERVER_PORT).toBool()) { if (runControl->device()->sshForwardDebugServerPort()) {
QVariantHash extraData; QVariantHash extraData;
extraData[RemoteLinux::Constants::SshForwardPort] = port; extraData[RemoteLinux::Constants::SshForwardPort] = port;
extraData[RemoteLinux::Constants::DisableSharing] = true; extraData[RemoteLinux::Constants::DisableSharing] = true;

View File

@@ -30,6 +30,7 @@
#include <QDateTime> #include <QDateTime>
#include <QReadWriteLock> #include <QReadWriteLock>
#include <QStandardItem>
#include <QString> #include <QString>
/*! /*!
@@ -115,6 +116,9 @@ const char HostKeyCheckingKey[] = "HostKeyChecking";
const char DebugServerKey[] = "DebugServerKey"; const char DebugServerKey[] = "DebugServerKey";
const char QmlRuntimeKey[] = "QmlsceneKey"; const char QmlRuntimeKey[] = "QmlsceneKey";
const char SshForwardDebugServerPortKey[] = "SshForwardDebugServerPort";
const char LinkDeviceKey[] = "LinkDevice";
using AuthType = SshParameters::AuthenticationType; using AuthType = SshParameters::AuthenticationType;
const AuthType DefaultAuthType = SshParameters::AuthenticationTypeAll; const AuthType DefaultAuthType = SshParameters::AuthenticationTypeAll;
const IDevice::MachineType DefaultMachineType = IDevice::Hardware; const IDevice::MachineType DefaultMachineType = IDevice::Hardware;
@@ -139,16 +143,14 @@ public:
Utils::SynchronizedValue<SshParameters> sshParameters; Utils::SynchronizedValue<SshParameters> sshParameters;
PortList freePorts;
QList<Icon> deviceIcons; QList<Icon> deviceIcons;
QList<IDevice::DeviceAction> deviceActions; QList<IDevice::DeviceAction> deviceActions;
Store extraData; Store extraData;
IDevice::OpenTerminal openTerminal; IDevice::OpenTerminal openTerminal;
Utils::StringAspect displayName; Utils::StringAspect displayName;
Utils::FilePathAspect debugServerPath;
Utils::FilePathAspect qmlRunCommand; SshParametersAspectContainer sshParametersAspectContainer;
bool isTesting = false; bool isTesting = false;
}; };
@@ -172,12 +174,49 @@ IDevice::IDevice()
{ {
setAutoApply(false); setAutoApply(false);
registerAspect(&d->sshParametersAspectContainer);
connect(&d->sshParametersAspectContainer, &AspectContainer::applied, this, [this]() {
*d->sshParameters.writeLocked() = d->sshParametersAspectContainer.sshParameters();
});
registerAspect(&d->displayName); registerAspect(&d->displayName);
d->displayName.setSettingsKey(DisplayNameKey); d->displayName.setSettingsKey(DisplayNameKey);
d->displayName.setDisplayStyle(StringAspect::DisplayStyle::LineEditDisplay); d->displayName.setDisplayStyle(StringAspect::DisplayStyle::LineEditDisplay);
// allowEmptyCommand.setSettingsKey() intentionally omitted, this is not persisted. // allowEmptyCommand.setSettingsKey() intentionally omitted, this is not persisted.
sshForwardDebugServerPort.setSettingsKey(SshForwardDebugServerPortKey);
sshForwardDebugServerPort.setLabelText(Tr::tr("Use SSH port forwarding for debugging"));
sshForwardDebugServerPort.setToolTip(
Tr::tr("Enable debugging on remote targets which cannot expose gdbserver ports.\n"
"The ssh tunneling is used to map the remote gdbserver port to localhost.\n"
"The local and remote ports are determined automatically."));
sshForwardDebugServerPort.setDefaultValue(false);
sshForwardDebugServerPort.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBox);
linkDevice.setSettingsKey(LinkDeviceKey);
linkDevice.setLabelText(Tr::tr("Access via:"));
linkDevice.setToolTip(Tr::tr("Select the device to connect through."));
linkDevice.setDefaultValue("Direct");
linkDevice.setComboBoxEditable(false);
linkDevice.setFillCallback([this](const StringSelectionAspect::ResultCallback &cb) {
auto dm = DeviceManager::instance();
QList<QStandardItem *> items;
auto defaultItem = new QStandardItem(Tr::tr("Direct"));
defaultItem->setData("direct");
items.append(defaultItem);
for (int i = 0, n = dm->deviceCount(); i < n; ++i) {
const auto device = dm->deviceAt(i);
if (device->id() == this->id())
continue;
QStandardItem *newItem = new QStandardItem(device->displayName());
newItem->setData(device->id().toSetting());
items.append(newItem);
}
cb(items);
});
auto validateDisplayName = [](const QString &old, const QString &newValue) -> Result<> { auto validateDisplayName = [](const QString &old, const QString &newValue) -> Result<> {
if (old == newValue) if (old == newValue)
return ResultOk; return ResultOk;
@@ -205,11 +244,27 @@ IDevice::IDevice()
return newValue; return newValue;
}); });
registerAspect(&d->debugServerPath); debugServerPathAspect.setSettingsKey(DebugServerKey);
d->debugServerPath.setSettingsKey(DebugServerKey); debugServerPathAspect.setLabelText(Tr::tr("GDB server executable:"));
debugServerPathAspect.setToolTip(Tr::tr("The GDB server executable to use on the device."));
debugServerPathAspect.setPlaceHolderText(Tr::tr("Leave empty to look up executable in $PATH"));
debugServerPathAspect.setHistoryCompleter("GdbServer");
debugServerPathAspect.setAllowPathFromDevice(true);
debugServerPathAspect.setExpectedKind(PathChooser::ExistingCommand);
registerAspect(&d->qmlRunCommand); qmlRunCommandAspect.setSettingsKey(QmlRuntimeKey);
d->qmlRunCommand.setSettingsKey(QmlRuntimeKey); qmlRunCommandAspect.setLabelText(Tr::tr("QML runtime executable:"));
qmlRunCommandAspect.setToolTip(Tr::tr("The QML runtime executable to use on the device."));
qmlRunCommandAspect.setPlaceHolderText(Tr::tr("Leave empty to look up executable in $PATH"));
qmlRunCommandAspect.setHistoryCompleter("QmlRuntime");
qmlRunCommandAspect.setAllowPathFromDevice(true);
qmlRunCommandAspect.setExpectedKind(PathChooser::ExistingCommand);
freePortsAspect.setSettingsKey(PortsSpecKey);
freePortsAspect.setLabelText(Tr::tr("Free ports:"));
freePortsAspect.setToolTip(
Tr::tr("You can enter lists and ranges like this: '1024,1026-1028,1030'."));
freePortsAspect.setHistoryCompleter("PortRange");
} }
IDevice::~IDevice() = default; IDevice::~IDevice() = default;
@@ -258,22 +313,22 @@ FilePath IDevice::filePath(const QString &pathOnDevice) const
FilePath IDevice::debugServerPath() const FilePath IDevice::debugServerPath() const
{ {
return d->debugServerPath(); return debugServerPathAspect();
} }
void IDevice::setDebugServerPath(const FilePath &path) void IDevice::setDebugServerPath(const FilePath &path)
{ {
d->debugServerPath.setValue(path); debugServerPathAspect.setValue(path);
} }
FilePath IDevice::qmlRunCommand() const FilePath IDevice::qmlRunCommand() const
{ {
return d->qmlRunCommand(); return qmlRunCommandAspect();
} }
void IDevice::setQmlRunCommand(const FilePath &path) void IDevice::setQmlRunCommand(const FilePath &path)
{ {
d->qmlRunCommand.setValue(path); qmlRunCommandAspect.setValue(path);
} }
bool IDevice::handlesFile(const FilePath &filePath) const bool IDevice::handlesFile(const FilePath &filePath) const
@@ -492,12 +547,34 @@ Id IDevice::idFromMap(const Store &map)
return Id::fromSetting(map.value(IdKey)); return Id::fromSetting(map.value(IdKey));
} }
// Backwards compatibility: Pre 17.0 a bunch of settings were stored in the extra data
namespace {
static const char LinkDevice[] = "RemoteLinux.LinkDevice";
static const char SSH_FORWARD_DEBUGSERVER_PORT[] = "RemoteLinux.SshForwardDebugServerPort";
static void backwardsFromExtraData(IDevice *device, const Store &map)
{
if (map.contains(LinkDevice))
device->linkDevice.setValue(Id::fromSetting(map.value(LinkDevice)).toString());
if (map.contains(SSH_FORWARD_DEBUGSERVER_PORT))
device->sshForwardDebugServerPort.setValue(map.value(SSH_FORWARD_DEBUGSERVER_PORT).toBool());
}
static void backwardsToExtraData(const IDevice *const device, Store &map)
{
map.insert(LinkDevice, device->linkDevice());
map.insert(SSH_FORWARD_DEBUGSERVER_PORT, device->sshForwardDebugServerPort());
}
} // namespace
/*! /*!
Restores a device object from a serialized state as written by toMap(). Restores a device object from a serialized state as written by toMap().
If subclasses override this to restore additional state, they must call the If subclasses override this to restore additional state, they must call the
base class implementation. base class implementation.
*/ */
void IDevice::fromMap(const Store &map) void IDevice::fromMap(const Store &map)
{ {
AspectContainer::fromMap(map); AspectContainer::fromMap(map);
@@ -509,33 +586,32 @@ void IDevice::fromMap(const Store &map)
d->id = newId(); d->id = newId();
d->origin = static_cast<Origin>(map.value(OriginKey, ManuallyAdded).toInt()); d->origin = static_cast<Origin>(map.value(OriginKey, ManuallyAdded).toInt());
d->sshParameters.write([&map](SshParameters &ssh) {
ssh.setHost(map.value(HostKey).toString());
ssh.setPort(map.value(SshPortKey, 22).toInt());
ssh.setUserName(map.value(UserNameKey).toString());
// Pre-4.9, the authentication enum used to have more values
const int storedAuthType = map.value(AuthKey, DefaultAuthType).toInt();
const bool outdatedAuthType = storedAuthType > SshParameters::AuthenticationTypeSpecificKey;
ssh.setAuthenticationType(
outdatedAuthType ? SshParameters::AuthenticationTypeAll
: static_cast<AuthType>(storedAuthType));
ssh.setPrivateKeyFile(
FilePath::fromSettings(map.value(KeyFileKey, defaultPrivateKeyFilePath())));
ssh.setTimeout(map.value(TimeoutKey, DefaultTimeout).toInt());
ssh.setHostKeyCheckingMode(static_cast<SshHostKeyCheckingMode>(
map.value(HostKeyCheckingKey, SshHostKeyCheckingNone).toInt()));
});
QString portsSpec = map.value(PortsSpecKey).toString();
if (portsSpec.isEmpty())
portsSpec = "10000-10100";
d->freePorts = PortList::fromString(portsSpec);
d->machineType = static_cast<MachineType>(map.value(MachineTypeKey, DefaultMachineType).toInt()); d->machineType = static_cast<MachineType>(map.value(MachineTypeKey, DefaultMachineType).toInt());
d->version = map.value(VersionKey, 0).toInt(); d->version = map.value(VersionKey, 0).toInt();
d->extraData = storeFromVariant(map.value(ExtraDataKey)); d->extraData = storeFromVariant(map.value(ExtraDataKey));
backwardsFromExtraData(this, d->extraData);
SshParameters ssh;
ssh.setHost(map.value(HostKey).toString());
ssh.setPort(map.value(SshPortKey, 22).toInt());
ssh.setUserName(map.value(UserNameKey).toString());
// Pre-4.9, the authentication enum used to have more values
const int storedAuthType = map.value(AuthKey, DefaultAuthType).toInt();
const bool outdatedAuthType = storedAuthType > SshParameters::AuthenticationTypeSpecificKey;
ssh.setAuthenticationType(
outdatedAuthType ? SshParameters::AuthenticationTypeAll
: static_cast<AuthType>(storedAuthType));
ssh.setPrivateKeyFile(
FilePath::fromSettings(map.value(KeyFileKey, defaultPrivateKeyFilePath())));
ssh.setTimeout(map.value(TimeoutKey, DefaultTimeout).toInt());
ssh.setHostKeyCheckingMode(static_cast<SshHostKeyCheckingMode>(
map.value(HostKeyCheckingKey, SshHostKeyCheckingNone).toInt()));
d->sshParametersAspectContainer.setSshParameters(ssh);
} }
/*! /*!
@@ -554,21 +630,21 @@ void IDevice::toMap(Store &map) const
map.insert(OriginKey, d->origin); map.insert(OriginKey, d->origin);
map.insert(MachineTypeKey, d->machineType); map.insert(MachineTypeKey, d->machineType);
d->sshParameters.read([&map](const auto &ssh) {
map.insert(HostKey, ssh.host());
map.insert(SshPortKey, ssh.port());
map.insert(UserNameKey, ssh.userName());
map.insert(AuthKey, ssh.authenticationType());
map.insert(KeyFileKey, ssh.privateKeyFile().toSettings());
map.insert(TimeoutKey, ssh.timeout());
map.insert(HostKeyCheckingKey, ssh.hostKeyCheckingMode());
});
map.insert(PortsSpecKey, d->freePorts.toString());
map.insert(VersionKey, d->version); map.insert(VersionKey, d->version);
map.insert(ExtraDataKey, variantFromStore(d->extraData)); Store extraData = d->extraData;
backwardsToExtraData(this, extraData);
map.insert(ExtraDataKey, variantFromStore(extraData));
SshParameters ssh = d->sshParametersAspectContainer.sshParameters();
map.insert(HostKey, ssh.host());
map.insert(SshPortKey, ssh.port());
map.insert(UserNameKey, ssh.userName());
map.insert(AuthKey, ssh.authenticationType());
map.insert(KeyFileKey, ssh.privateKeyFile().toSettings());
map.insert(TimeoutKey, ssh.timeout());
map.insert(HostKeyCheckingKey, ssh.hostKeyCheckingMode());
} }
IDevice::Ptr IDevice::clone() const IDevice::Ptr IDevice::clone() const
@@ -639,9 +715,23 @@ SshParameters IDevice::sshParameters() const
return *d->sshParameters.readLocked(); return *d->sshParameters.readLocked();
} }
void IDevice::setSshParameters(const SshParameters &sshParameters) void IDevice::setDefaultSshParameters(const SshParameters &sshParameters)
{ {
*d->sshParameters.writeLocked() = sshParameters; QTC_ASSERT(QThread::currentThread() == qApp->thread(),
return); // This is not thread-safe.
sshParametersAspectContainer().host.setDefaultValue(sshParameters.host());
sshParametersAspectContainer().port.setDefaultValue(sshParameters.port());
sshParametersAspectContainer().userName.setDefaultValue(sshParameters.userName());
sshParametersAspectContainer().privateKeyFile.setDefaultPathValue(
sshParameters.privateKeyFile());
sshParametersAspectContainer().timeout.setDefaultValue(sshParameters.timeout());
sshParametersAspectContainer().authenticationType.setDefaultValue(
sshParameters.authenticationType());
sshParametersAspectContainer().hostKeyCheckingMode.setDefaultValue(
sshParameters.hostKeyCheckingMode());
*d->sshParameters.writeLocked() = sshParametersAspectContainer().sshParameters();
} }
QUrl IDevice::toolControlChannel(const ControlChannelHint &) const QUrl IDevice::toolControlChannel(const ControlChannelHint &) const
@@ -654,12 +744,12 @@ QUrl IDevice::toolControlChannel(const ControlChannelHint &) const
void IDevice::setFreePorts(const PortList &freePorts) void IDevice::setFreePorts(const PortList &freePorts)
{ {
d->freePorts = freePorts; freePortsAspect.setPortList(freePorts);
} }
PortList IDevice::freePorts() const PortList IDevice::freePorts() const
{ {
return d->freePorts; return freePortsAspect.portList();
} }
IDevice::MachineType IDevice::machineType() const IDevice::MachineType IDevice::machineType() const
@@ -813,6 +903,13 @@ QVariant DeviceConstRef::extraData(Id kind) const
return device->extraData(kind); return device->extraData(kind);
} }
Id DeviceConstRef::linkDeviceId() const
{
const IDevice::ConstPtr device = m_constDevice.lock();
QTC_ASSERT(device, return {});
return Id::fromString(device->linkDevice.value());
}
FilePath DeviceConstRef::filePath(const QString &pathOnDevice) const FilePath DeviceConstRef::filePath(const QString &pathOnDevice) const
{ {
const IDevice::ConstPtr device = m_constDevice.lock(); const IDevice::ConstPtr device = m_constDevice.lock();
@@ -842,7 +939,12 @@ void DeviceRef::setSshParameters(const SshParameters &params)
{ {
const IDevice::Ptr device = m_mutableDevice.lock(); const IDevice::Ptr device = m_mutableDevice.lock();
QTC_ASSERT(device, return); QTC_ASSERT(device, return);
device->setSshParameters(params); device->setDefaultSshParameters(params);
}
SshParametersAspectContainer &IDevice::sshParametersAspectContainer() const
{
return d->sshParametersAspectContainer;
} }
} // namespace ProjectExplorer } // namespace ProjectExplorer

View File

@@ -45,6 +45,7 @@ class FileTransferInterface;
class FileTransferSetupData; class FileTransferSetupData;
class Kit; class Kit;
class SshParameters; class SshParameters;
class SshParametersAspectContainer;
class Target; class Target;
class Task; class Task;
@@ -157,7 +158,9 @@ public:
static QString defaultPublicKeyFilePath(); static QString defaultPublicKeyFilePath();
SshParameters sshParameters() const; SshParameters sshParameters() const;
void setSshParameters(const SshParameters &sshParameters); void setDefaultSshParameters(const SshParameters &sshParameters);
SshParametersAspectContainer &sshParametersAspectContainer() const;
enum ControlChannelHint { QmlControlChannel }; enum ControlChannelHint { QmlControlChannel };
virtual QUrl toolControlChannel(const ControlChannelHint &) const; virtual QUrl toolControlChannel(const ControlChannelHint &) const;
@@ -186,8 +189,6 @@ public:
Utils::Result<> openTerminal(const Utils::Environment &env, Utils::Result<> openTerminal(const Utils::Environment &env,
const Utils::FilePath &workingDir) const; const Utils::FilePath &workingDir) const;
Utils::BoolAspect allowEmptyCommand{this};
bool isWindowsDevice() const { return osType() == Utils::OsTypeWindows; } bool isWindowsDevice() const { return osType() == Utils::OsTypeWindows; }
bool isLinuxDevice() const { return osType() == Utils::OsTypeLinux; } bool isLinuxDevice() const { return osType() == Utils::OsTypeLinux; }
bool isMacDevice() const { return osType() == Utils::OsTypeMac; } bool isMacDevice() const { return osType() == Utils::OsTypeMac; }
@@ -219,6 +220,14 @@ public:
void doApply() const; void doApply() const;
public:
Utils::BoolAspect allowEmptyCommand{this};
Utils::StringSelectionAspect linkDevice{this};
Utils::BoolAspect sshForwardDebugServerPort{this};
Utils::FilePathAspect debugServerPathAspect{this};
Utils::FilePathAspect qmlRunCommandAspect{this};
Utils::PortListAspect freePortsAspect{this};
protected: protected:
IDevice(); IDevice();
@@ -257,6 +266,7 @@ public:
SshParameters sshParameters() const; SshParameters sshParameters() const;
Utils::FilePath filePath(const QString &pathOnDevice) const; Utils::FilePath filePath(const QString &pathOnDevice) const;
QVariant extraData(Utils::Id kind) const; QVariant extraData(Utils::Id kind) const;
Utils::Id linkDeviceId() const;
private: private:
std::weak_ptr<const IDevice> m_constDevice; std::weak_ptr<const IDevice> m_constDevice;

View File

@@ -3,6 +3,7 @@
#include "sshparameters.h" #include "sshparameters.h"
#include "../projectexplorertr.h"
#include "sshsettings.h" #include "sshsettings.h"
#include <utils/environment.h> #include <utils/environment.h>
@@ -59,7 +60,7 @@ QStringList SshParameters::connectionOptions(const FilePath &binary) const
args << "-o" << "User=" + userName(); args << "-o" << "User=" + userName();
const bool keyOnly = m_authenticationType == SshParameters::AuthenticationTypeSpecificKey; const bool keyOnly = m_authenticationType == SshParameters::AuthenticationTypeSpecificKey;
if (keyOnly) if (keyOnly && m_privateKeyFile.isReadableFile())
args << "-o" << "IdentitiesOnly=yes" << "-i" << m_privateKeyFile.path(); args << "-o" << "IdentitiesOnly=yes" << "-i" << m_privateKeyFile.path();
const QString batchModeEnabled = (keyOnly || SshSettings::askpassFilePath().isEmpty()) const QString batchModeEnabled = (keyOnly || SshSettings::askpassFilePath().isEmpty())
@@ -216,4 +217,90 @@ void printSetupHelp()
} // namespace SshTest } // namespace SshTest
#endif #endif
void SshParametersAspectContainer::setSshParameters(const SshParameters &params)
{
QTC_ASSERT(QThread::currentThread() == thread(), return);
host.setVolatileValue(params.host());
port.setVolatileValue(params.port());
userName.setVolatileValue(params.userName());
privateKeyFile.setVolatileValue(params.privateKeyFile().toUserOutput());
timeout.setVolatileValue(params.timeout());
authenticationType.setVolatileValue(params.authenticationType());
hostKeyCheckingMode.setVolatileValue(params.hostKeyCheckingMode());
privateKeyFile.setEnabled(
params.authenticationType() == SshParameters::AuthenticationTypeSpecificKey);
// This will emit the applied signal which the IDevice uses to update the ssh parameters.
apply();
}
SshParameters SshParametersAspectContainer::sshParameters() const
{
QTC_ASSERT(QThread::currentThread() == thread(), return SshParameters());
SshParameters params;
params.setHost(host.expandedValue());
params.setPort(port.value());
params.setUserName(userName.expandedValue());
params.setPrivateKeyFile(privateKeyFile.expandedValue());
params.setTimeout(timeout.value());
params.setAuthenticationType(authenticationType.value());
params.setHostKeyCheckingMode(hostKeyCheckingMode.value());
return params;
}
SshParametersAspectContainer::SshParametersAspectContainer()
{
authenticationType.setDefaultValue(SshParameters::AuthenticationTypeAll);
authenticationType.setDisplayStyle(SelectionAspect::DisplayStyle::RadioButtons);
authenticationType
.addOption(Tr::tr("Default"), Tr::tr("Use all available authentication methods"));
authenticationType
.addOption(Tr::tr("Specific &key"), Tr::tr("Use only the specified private key"));
authenticationType.setToolTip(Tr::tr("Select the authentication method to use"));
authenticationType.setLabelText(Tr::tr("Authentication type:"));
hostKeyCheckingMode.setToolTip(Tr::tr("The device's SSH host key checking mode"));
hostKeyCheckingMode.setLabelText(Tr::tr("Host key check:"));
hostKeyCheckingMode.setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox);
hostKeyCheckingMode.addOption("None", Tr::tr("No host key checking"));
hostKeyCheckingMode.addOption("Allow No Match", Tr::tr("Allow host key checking"));
hostKeyCheckingMode.addOption("Strict", Tr::tr("Strict host key checking"));
host.setDisplayStyle(StringAspect::DisplayStyle::LineEditDisplay);
host.setPlaceHolderText(Tr::tr("Host name or IP address"));
host.setToolTip(Tr::tr("The device's host name or IP address"));
host.setHistoryCompleter("HostName");
host.setLabelText(Tr::tr("Host name:"));
userName.setDisplayStyle(StringAspect::DisplayStyle::LineEditDisplay);
userName.setPlaceHolderText(Tr::tr("User name"));
userName.setToolTip(Tr::tr("The device's SSH user name"));
userName.setHistoryCompleter("UserName");
userName.setLabelText(Tr::tr("User name:"));
port.setDefaultValue(22);
port.setRange(1, 65535);
port.setToolTip(Tr::tr("The device's SSH port number"));
port.setLabelText(Tr::tr("SSH port:"));
privateKeyFile.setPlaceHolderText(Tr::tr("Private key file"));
privateKeyFile.setToolTip(Tr::tr("The device's private key file"));
privateKeyFile.setLabelText(Tr::tr("Private key file:"));
privateKeyFile.setHistoryCompleter("KeyFile");
privateKeyFile.setEnabled(
authenticationType.volatileValue() == SshParameters::AuthenticationTypeSpecificKey);
connect(&authenticationType, &SelectionAspect::volatileValueChanged, this, [this]() {
privateKeyFile.setEnabled(
authenticationType.volatileValue() == SshParameters::AuthenticationTypeSpecificKey);
});
timeout.setDefaultValue(10);
timeout.setLabelText(Tr::tr("Timeout:"));
timeout.setToolTip(Tr::tr("The device's SSH connection timeout"));
}
} // namespace ProjectExplorer } // namespace ProjectExplorer

View File

@@ -73,6 +73,25 @@ private:
QString m_userName; QString m_userName;
}; };
class PROJECTEXPLORER_EXPORT SshParametersAspectContainer : public Utils::AspectContainer
{
public:
SshParametersAspectContainer();
SshParameters sshParameters() const;
void setSshParameters(const SshParameters &params);
public:
Utils::FilePathAspect privateKeyFile{this};
Utils::IntegerAspect timeout{this};
Utils::TypedSelectionAspect<SshParameters::AuthenticationType> authenticationType{this};
Utils::TypedSelectionAspect<SshHostKeyCheckingMode> hostKeyCheckingMode{this};
Utils::StringAspect host{this};
Utils::IntegerAspect port{this};
Utils::StringAspect userName{this};
};
#ifdef WITH_TESTS #ifdef WITH_TESTS
namespace SshTest { namespace SshTest {
const QString PROJECTEXPLORER_EXPORT getHostFromEnvironment(); const QString PROJECTEXPLORER_EXPORT getHostFromEnvironment();

View File

@@ -231,7 +231,6 @@ const char USER_ENVIRONMENT_CHANGES_KEY[] = "ProjectExplorer.BuildConfiguration.
// Called "RemoteLinux." for backwards compatibility // Called "RemoteLinux." for backwards compatibility
const char SUPPORTS_RSYNC[] = "RemoteLinux.SupportsRSync"; const char SUPPORTS_RSYNC[] = "RemoteLinux.SupportsRSync";
const char SUPPORTS_SFTP[] = "RemoteLinux.SupportsSftp"; const char SUPPORTS_SFTP[] = "RemoteLinux.SupportsSftp";
const char SSH_FORWARD_DEBUGSERVER_PORT[] = "RemoteLinux.SshForwardDebugServerPort";
// SDKs related ids: // SDKs related ids:
const char SDK_SETTINGS_CATEGORY[] = "AN.SDKs"; const char SDK_SETTINGS_CATEGORY[] = "AN.SDKs";

View File

@@ -634,7 +634,7 @@ void RunControlPrivate::startPortsGathererIfNeededAndContinueStart()
QUrl RunControlPrivate::getNextChannel(PortList *portList, const QList<Port> &usedPorts) QUrl RunControlPrivate::getNextChannel(PortList *portList, const QList<Port> &usedPorts)
{ {
QUrl result; QUrl result;
if (q->device()->extraData(Constants::SSH_FORWARD_DEBUGSERVER_PORT).toBool()) { if (q->device()->sshForwardDebugServerPort()) {
result.setScheme(urlTcpScheme()); result.setScheme(urlTcpScheme());
result.setHost("localhost"); result.setHost("localhost");
} else { } else {

View File

@@ -66,9 +66,9 @@ public:
setMachineType(IDevice::Hardware); setMachineType(IDevice::Hardware);
SshParameters sshParams; SshParameters sshParams;
sshParams.setTimeout(10); sshParams.setTimeout(10);
setSshParameters(sshParams); setDefaultSshParameters(sshParams);
setFreePorts(PortList::fromString("10000-10100")); setFreePorts(PortList::fromString("10000-10100"));
setExtraData(RemoteLinux::Constants::SourceProfile, true); sourceProfile.setDefaultValue(true);
addDeviceAction({Tr::tr("Deploy Qt libraries..."), [](const IDevice::Ptr &device) { addDeviceAction({Tr::tr("Deploy Qt libraries..."), [](const IDevice::Ptr &device) {
QnxDeployQtLibrariesDialog dialog(device, Core::ICore::dialogParent()); QnxDeployQtLibrariesDialog dialog(device, Core::ICore::dialogParent());

View File

@@ -47,7 +47,7 @@ TestLinuxDeviceFactory::TestLinuxDeviceFactory()
device->setupId(IDevice::ManuallyAdded); device->setupId(IDevice::ManuallyAdded);
device->setType("test"); device->setType("test");
qDebug() << "device : " << device->type(); qDebug() << "device : " << device->type();
device->setSshParameters(SshTest::getParameters()); device->sshParametersAspectContainer().setSshParameters(SshTest::getParameters());
return device; return device;
}); });
} }

View File

@@ -3,6 +3,7 @@
#include "genericlinuxdeviceconfigurationwidget.h" #include "genericlinuxdeviceconfigurationwidget.h"
#include "linuxdevice.h"
#include "remotelinux_constants.h" #include "remotelinux_constants.h"
#include "remotelinuxtr.h" #include "remotelinuxtr.h"
#include "sshkeycreationdialog.h" #include "sshkeycreationdialog.h"
@@ -33,325 +34,62 @@ using namespace Utils;
namespace RemoteLinux::Internal { namespace RemoteLinux::Internal {
GenericLinuxDeviceConfigurationWidget::GenericLinuxDeviceConfigurationWidget( GenericLinuxDeviceConfigurationWidget::GenericLinuxDeviceConfigurationWidget(
const IDevice::Ptr &device) : const IDevice::Ptr &device)
IDeviceWidget(device) : IDeviceWidget(device)
{ {
m_defaultAuthButton = new QRadioButton(Tr::tr("Default"), this);
m_keyButton = new QRadioButton(Tr::tr("Specific &key"));
m_hostLineEdit = new FancyLineEdit(this);
m_hostLineEdit->setPlaceholderText(Tr::tr("IP or host name of the device"));
m_hostLineEdit->setHistoryCompleter("HostName");
m_sshPortSpinBox = new QSpinBox(this);
m_sshPortSpinBox->setMinimum(0);
m_sshPortSpinBox->setMaximum(65535);
m_sshPortSpinBox->setValue(22);
m_hostKeyCheckBox = new QCheckBox(Tr::tr("&Check host key"));
m_portsLineEdit = new FancyLineEdit(this);
m_portsLineEdit->setToolTip(Tr::tr("You can enter lists and ranges like this: '1024,1026-1028,1030'."));
m_portsLineEdit->setHistoryCompleter("PortRange");
m_portsWarningLabel = new QLabel(this);
m_timeoutSpinBox = new QSpinBox(this);
m_timeoutSpinBox->setMaximum(10000);
m_timeoutSpinBox->setSingleStep(10);
m_timeoutSpinBox->setValue(1000);
m_timeoutSpinBox->setSuffix(Tr::tr("s"));
m_userLineEdit = new FancyLineEdit(this);
m_userLineEdit->setHistoryCompleter("UserName");
m_keyLabel = new QLabel(Tr::tr("Private key file:"));
m_keyFileLineEdit = new PathChooser(this);
auto createKeyButton = new QPushButton(Tr::tr("Create New...")); auto createKeyButton = new QPushButton(Tr::tr("Create New..."));
m_machineTypeValueLabel = new QLabel(this); const QString machineType = device->machineType() == IDevice::Hardware
? Tr::tr("Physical Device")
const QString hint = Tr::tr("Leave empty to look up executable in $PATH"); : Tr::tr("Emulator");
m_gdbServerLineEdit = new PathChooser(this); auto linuxDevice = std::dynamic_pointer_cast<LinuxDevice>(device);
m_gdbServerLineEdit->setExpectedKind(PathChooser::ExistingCommand); QTC_ASSERT(linuxDevice, return);
m_gdbServerLineEdit->setPlaceholderText(hint);
m_gdbServerLineEdit->setToolTip(hint);
m_gdbServerLineEdit->setHistoryCompleter("GdbServer");
m_gdbServerLineEdit->setAllowPathFromDevice(true);
m_qmlRuntimeLineEdit = new PathChooser(this);
m_qmlRuntimeLineEdit->setExpectedKind(PathChooser::ExistingCommand);
m_qmlRuntimeLineEdit->setPlaceholderText(hint);
m_qmlRuntimeLineEdit->setToolTip(hint);
m_qmlRuntimeLineEdit->setHistoryCompleter("QmlRuntime");
m_qmlRuntimeLineEdit->setAllowPathFromDevice(true);
m_sourceProfileCheckBox =
new QCheckBox(Tr::tr("Source %1 and %2").arg("/etc/profile").arg("$HOME/.profile"));
m_linkDeviceComboBox = new QComboBox;
m_linkDeviceComboBox->addItem(Tr::tr("Direct"), QVariant());
auto dm = DeviceManager::instance();
const int dmCount = dm->deviceCount();
for (int i = 0; i < dmCount; ++i) {
IDevice::ConstPtr dev = dm->deviceAt(i);
if (dev->id() != device->id())
m_linkDeviceComboBox->addItem(dev->displayName(), dev->id().toSetting());
}
auto sshPortLabel = new QLabel(Tr::tr("&SSH port:"));
sshPortLabel->setBuddy(m_sshPortSpinBox);
m_useSshPortForwardingForDebugging = new QCheckBox;
m_useSshPortForwardingForDebugging->setText(Tr::tr("Use SSH port forwarding for debugging"));
m_useSshPortForwardingForDebugging->setToolTip(
Tr::tr("Enable debugging on remote targets which cannot expose gdbserver ports.\n"
"The ssh tunneling is used to map the remote gdbserver port to localhost.\n"
"The local and remote ports are determined automatically."));
using namespace Layouting; using namespace Layouting;
auto portWarningLabel = new QLabel(
QString("<font color=\"red\">%1</font>").arg(Tr::tr("You will need at least one port.")));
auto updatePortWarningLabel = [portWarningLabel, device]() {
portWarningLabel->setVisible(device->freePortsAspect.volatileValue().isEmpty());
};
updatePortWarningLabel();
// clang-format off
connect(&device->freePortsAspect, &PortListAspect::volatileValueChanged, this, updatePortWarningLabel);
Form { Form {
Tr::tr("Machine type:"), m_machineTypeValueLabel, st, br, Tr::tr("Machine type:"), machineType, st, br,
Tr::tr("Authentication type:"), m_defaultAuthButton, m_keyButton, st, br, device->sshParametersAspectContainer().authenticationType.labelText(), device->sshParametersAspectContainer().authenticationType, st, br,
Tr::tr("&Host name:"), m_hostLineEdit, sshPortLabel, m_sshPortSpinBox, m_hostKeyCheckBox, st, br, device->sshParametersAspectContainer().host, device->sshParametersAspectContainer().port, device->sshParametersAspectContainer().hostKeyCheckingMode, st, br,
Tr::tr("Free ports:"), m_portsLineEdit, m_portsWarningLabel, Tr::tr("Timeout:"), m_timeoutSpinBox, st, br, device->freePortsAspect, portWarningLabel, device->sshParametersAspectContainer().timeout, st, br,
Tr::tr("&Username:"), m_userLineEdit, st, br, device->sshParametersAspectContainer().userName, st, br,
m_keyLabel, m_keyFileLineEdit, createKeyButton, br, device->sshParametersAspectContainer().privateKeyFile, createKeyButton, br,
Tr::tr("GDB server executable:"), m_gdbServerLineEdit, br, device->debugServerPathAspect, br,
Tr::tr("QML runtime executable:"), m_qmlRuntimeLineEdit, br, device->qmlRunCommandAspect, br,
QString(), m_sourceProfileCheckBox, br, linuxDevice->sourceProfile, br,
QString(), m_useSshPortForwardingForDebugging, br, device->sshForwardDebugServerPort, br,
Tr::tr("Access via:"), m_linkDeviceComboBox, br, device->linkDevice, br,
}.attachTo(this); }.attachTo(this);
// clang-format on
connect(m_hostLineEdit, &QLineEdit::editingFinished, connect(
this, &GenericLinuxDeviceConfigurationWidget::hostNameEditingFinished); createKeyButton,
connect(m_userLineEdit, &QLineEdit::editingFinished, &QAbstractButton::clicked,
this, &GenericLinuxDeviceConfigurationWidget::userNameEditingFinished); this,
connect(m_keyFileLineEdit, &PathChooser::editingFinished, &GenericLinuxDeviceConfigurationWidget::createNewKey);
this, &GenericLinuxDeviceConfigurationWidget::keyFileEditingFinished);
connect(m_keyFileLineEdit, &PathChooser::browsingFinished,
this, &GenericLinuxDeviceConfigurationWidget::keyFileEditingFinished);
connect(m_keyButton, &QAbstractButton::toggled,
this, &GenericLinuxDeviceConfigurationWidget::authenticationTypeChanged);
connect(m_timeoutSpinBox, &QAbstractSpinBox::editingFinished,
this, &GenericLinuxDeviceConfigurationWidget::timeoutEditingFinished);
connect(m_timeoutSpinBox, &QSpinBox::valueChanged,
this, &GenericLinuxDeviceConfigurationWidget::timeoutEditingFinished);
connect(m_sshPortSpinBox, &QAbstractSpinBox::editingFinished,
this, &GenericLinuxDeviceConfigurationWidget::sshPortEditingFinished);
connect(m_sshPortSpinBox, &QSpinBox::valueChanged,
this, &GenericLinuxDeviceConfigurationWidget::sshPortEditingFinished);
connect(m_portsLineEdit, &QLineEdit::editingFinished,
this, &GenericLinuxDeviceConfigurationWidget::handleFreePortsChanged);
connect(createKeyButton, &QAbstractButton::clicked,
this, &GenericLinuxDeviceConfigurationWidget::createNewKey);
connect(m_gdbServerLineEdit, &PathChooser::editingFinished,
this, &GenericLinuxDeviceConfigurationWidget::gdbServerEditingFinished);
connect(m_qmlRuntimeLineEdit, &PathChooser::editingFinished,
this, &GenericLinuxDeviceConfigurationWidget::qmlRuntimeEditingFinished);
connect(m_hostKeyCheckBox, &QCheckBox::toggled,
this, &GenericLinuxDeviceConfigurationWidget::hostKeyCheckingChanged);
connect(m_sourceProfileCheckBox, &QCheckBox::toggled,
this, &GenericLinuxDeviceConfigurationWidget::sourceProfileCheckingChanged);
connect(m_linkDeviceComboBox, &QComboBox::currentIndexChanged,
this, &GenericLinuxDeviceConfigurationWidget::linkDeviceChanged);
connect(m_useSshPortForwardingForDebugging, &QCheckBox::toggled,
this, &GenericLinuxDeviceConfigurationWidget::sshPortForwardingForDebugging);
initGui();
} }
GenericLinuxDeviceConfigurationWidget::~GenericLinuxDeviceConfigurationWidget() = default; GenericLinuxDeviceConfigurationWidget::~GenericLinuxDeviceConfigurationWidget() = default;
void GenericLinuxDeviceConfigurationWidget::authenticationTypeChanged()
{
SshParameters sshParams = device()->sshParameters();
const bool useKeyFile = m_keyButton->isChecked();
sshParams.setAuthenticationType(
useKeyFile ? SshParameters::AuthenticationTypeSpecificKey
: SshParameters::AuthenticationTypeAll);
device()->setSshParameters(sshParams);
m_keyFileLineEdit->setEnabled(useKeyFile);
m_keyLabel->setEnabled(useKeyFile);
}
void GenericLinuxDeviceConfigurationWidget::hostNameEditingFinished()
{
SshParameters sshParams = device()->sshParameters();
sshParams.setHost(m_hostLineEdit->text().trimmed());
device()->setSshParameters(sshParams);
}
void GenericLinuxDeviceConfigurationWidget::sshPortEditingFinished()
{
SshParameters sshParams = device()->sshParameters();
sshParams.setPort(m_sshPortSpinBox->value());
device()->setSshParameters(sshParams);
}
void GenericLinuxDeviceConfigurationWidget::timeoutEditingFinished()
{
SshParameters sshParams = device()->sshParameters();
sshParams.setTimeout(m_timeoutSpinBox->value());
device()->setSshParameters(sshParams);
}
void GenericLinuxDeviceConfigurationWidget::userNameEditingFinished()
{
SshParameters sshParams = device()->sshParameters();
sshParams.setUserName(m_userLineEdit->text());
device()->setSshParameters(sshParams);
}
void GenericLinuxDeviceConfigurationWidget::keyFileEditingFinished()
{
SshParameters sshParams = device()->sshParameters();
sshParams.setPrivateKeyFile(m_keyFileLineEdit->filePath());
device()->setSshParameters(sshParams);
}
void GenericLinuxDeviceConfigurationWidget::gdbServerEditingFinished()
{
device()->setDebugServerPath(m_gdbServerLineEdit->filePath());
}
void GenericLinuxDeviceConfigurationWidget::qmlRuntimeEditingFinished()
{
device()->setQmlRunCommand(m_qmlRuntimeLineEdit->filePath());
}
void GenericLinuxDeviceConfigurationWidget::handleFreePortsChanged()
{
device()->setFreePorts(PortList::fromString(m_portsLineEdit->text()));
updatePortsWarningLabel();
}
void GenericLinuxDeviceConfigurationWidget::setPrivateKey(const FilePath &path)
{
m_keyFileLineEdit->setFilePath(path);
keyFileEditingFinished();
}
void GenericLinuxDeviceConfigurationWidget::createNewKey() void GenericLinuxDeviceConfigurationWidget::createNewKey()
{ {
SshKeyCreationDialog dialog(this); SshKeyCreationDialog dialog(this);
if (dialog.exec() == QDialog::Accepted) if (dialog.exec() == QDialog::Accepted) {
setPrivateKey(dialog.privateKeyFilePath()); device()->sshParametersAspectContainer().privateKeyFile.setValue(
} dialog.privateKeyFilePath());
void GenericLinuxDeviceConfigurationWidget::hostKeyCheckingChanged(bool doCheck)
{
SshParameters sshParams = device()->sshParameters();
sshParams.setHostKeyCheckingMode(
doCheck ? SshHostKeyCheckingAllowNoMatch : SshHostKeyCheckingNone);
device()->setSshParameters(sshParams);
}
void GenericLinuxDeviceConfigurationWidget::sourceProfileCheckingChanged(bool doCheck)
{
device()->setExtraData(Constants::SourceProfile, doCheck);
}
void GenericLinuxDeviceConfigurationWidget::linkDeviceChanged(int index)
{
const QVariant deviceId = m_linkDeviceComboBox->itemData(index);
device()->setExtraData(Constants::LinkDevice, deviceId);
}
void GenericLinuxDeviceConfigurationWidget::sshPortForwardingForDebugging(bool on)
{
device()->setExtraData(ProjectExplorer::Constants::SSH_FORWARD_DEBUGSERVER_PORT, on);
}
void GenericLinuxDeviceConfigurationWidget::updateDeviceFromUi()
{
hostNameEditingFinished();
sshPortEditingFinished();
timeoutEditingFinished();
userNameEditingFinished();
keyFileEditingFinished();
handleFreePortsChanged();
gdbServerEditingFinished();
sshPortEditingFinished();
timeoutEditingFinished();
sourceProfileCheckingChanged(m_sourceProfileCheckBox->isChecked());
linkDeviceChanged(m_linkDeviceComboBox->currentIndex());
sshPortForwardingForDebugging(m_useSshPortForwardingForDebugging->isChecked());
qmlRuntimeEditingFinished();
}
void GenericLinuxDeviceConfigurationWidget::updatePortsWarningLabel()
{
m_portsWarningLabel->setVisible(!device()->freePorts().hasMore());
}
void GenericLinuxDeviceConfigurationWidget::initGui()
{
if (device()->machineType() == IDevice::Hardware)
m_machineTypeValueLabel->setText(Tr::tr("Physical Device"));
else
m_machineTypeValueLabel->setText(Tr::tr("Emulator"));
m_portsWarningLabel->setPixmap(Utils::Icons::CRITICAL.pixmap());
m_portsWarningLabel->setToolTip(QLatin1String("<font color=\"red\">")
+ Tr::tr("You will need at least one port.") + QLatin1String("</font>"));
m_keyFileLineEdit->setExpectedKind(PathChooser::File);
m_keyFileLineEdit->setHistoryCompleter("Ssh.KeyFile.History");
m_keyFileLineEdit->lineEdit()->setMinimumWidth(0);
QRegularExpressionValidator * const portsValidator
= new QRegularExpressionValidator(QRegularExpression(PortList::regularExpression()), this);
m_portsLineEdit->setValidator(portsValidator);
const SshParameters &sshParams = device()->sshParameters();
switch (sshParams.authenticationType()) {
case SshParameters::AuthenticationTypeSpecificKey:
m_keyButton->setChecked(true);
break;
case SshParameters::AuthenticationTypeAll:
m_defaultAuthButton->setChecked(true);
break;
} }
m_timeoutSpinBox->setValue(sshParams.timeout());
m_hostLineEdit->setEnabled(!device()->isAutoDetected());
m_sshPortSpinBox->setEnabled(!device()->isAutoDetected());
m_hostKeyCheckBox->setChecked(sshParams.hostKeyCheckingMode() != SshHostKeyCheckingNone);
m_sourceProfileCheckBox->setChecked(device()->extraData(Constants::SourceProfile).toBool());
Id linkDeviceId = Id::fromSetting(device()->extraData(Constants::LinkDevice));
auto dm = DeviceManager::instance();
int found = -1;
int minus = 0;
for (int i = 0, n = dm->deviceCount(); i < n; ++i) {
const auto otherId = dm->deviceAt(i)->id();
if (otherId == linkDeviceId) {
found = i;
break;
} else if (otherId == device()->id()) {
// Since we ourselves do not appear in the combo box, we need to adjust the index.
minus = 1;
}
}
m_linkDeviceComboBox->setCurrentIndex(found + 1 - minus); // There's the "Direct" entry first.
m_hostLineEdit->setText(sshParams.host());
m_sshPortSpinBox->setValue(sshParams.port());
m_portsLineEdit->setText(device()->freePorts().toString());
m_timeoutSpinBox->setValue(sshParams.timeout());
m_userLineEdit->setText(sshParams.userName());
m_keyFileLineEdit->setFilePath(sshParams.privateKeyFile());
m_keyFileLineEdit->setEnabled(
sshParams.authenticationType() == SshParameters::AuthenticationTypeSpecificKey);
m_gdbServerLineEdit->setFilePath(device()->debugServerPath());
m_qmlRuntimeLineEdit->setFilePath(device()->qmlRunCommand());
m_useSshPortForwardingForDebugging->setChecked(
device()->extraData(ProjectExplorer::Constants::SSH_FORWARD_DEBUGSERVER_PORT).toBool());
updatePortsWarningLabel();
} }
} // RemoteLinux::Internal } // RemoteLinux::Internal

View File

@@ -31,43 +31,8 @@ public:
~GenericLinuxDeviceConfigurationWidget() override; ~GenericLinuxDeviceConfigurationWidget() override;
private: private:
void authenticationTypeChanged();
void hostNameEditingFinished();
void sshPortEditingFinished();
void timeoutEditingFinished();
void userNameEditingFinished();
void keyFileEditingFinished();
void gdbServerEditingFinished();
void qmlRuntimeEditingFinished();
void handleFreePortsChanged();
void setPrivateKey(const Utils::FilePath &path);
void createNewKey(); void createNewKey();
void hostKeyCheckingChanged(bool doCheck); void updateDeviceFromUi() override {}
void sourceProfileCheckingChanged(bool doCheck);
void linkDeviceChanged(int index);
void sshPortForwardingForDebugging(bool on);
void updateDeviceFromUi() override;
void updatePortsWarningLabel();
void initGui();
QRadioButton *m_defaultAuthButton;
QLabel *m_keyLabel;
QRadioButton *m_keyButton;
Utils::FancyLineEdit *m_hostLineEdit;
QSpinBox *m_sshPortSpinBox;
QCheckBox *m_hostKeyCheckBox;
Utils::FancyLineEdit *m_portsLineEdit;
QLabel *m_portsWarningLabel;
Utils::FancyLineEdit *m_userLineEdit;
QSpinBox *m_timeoutSpinBox;
Utils::PathChooser *m_keyFileLineEdit;
QLabel *m_machineTypeValueLabel;
Utils::PathChooser *m_gdbServerLineEdit;
Utils::PathChooser *m_qmlRuntimeLineEdit;
QCheckBox *m_sourceProfileCheckBox;
QComboBox *m_linkDeviceComboBox;
QCheckBox *m_useSshPortForwardingForDebugging;
}; };
} // RemoteLinux::Internal } // RemoteLinux::Internal

View File

@@ -656,7 +656,7 @@ void SshProcessInterfacePrivate::start()
{ {
m_sshParameters = m_device->sshParameters(); m_sshParameters = m_device->sshParameters();
const Id linkDeviceId = Id::fromSetting(m_device->extraData(Constants::LinkDevice)); const Id linkDeviceId = Id::fromSetting(m_device->linkDevice.value());
if (const IDevice::ConstPtr linkDevice = DeviceManager::instance()->find(linkDeviceId)) { if (const IDevice::ConstPtr linkDevice = DeviceManager::instance()->find(linkDeviceId)) {
CommandLine cmd{linkDevice->filePath("ssh")}; CommandLine cmd{linkDevice->filePath("ssh")};
if (!m_sshParameters.userName().isEmpty()) { if (!m_sshParameters.userName().isEmpty()) {
@@ -775,10 +775,13 @@ void SshProcessInterfacePrivate::doStart()
CommandLine SshProcessInterfacePrivate::fullLocalCommandLine() const CommandLine SshProcessInterfacePrivate::fullLocalCommandLine() const
{ {
auto linuxDevice = std::dynamic_pointer_cast<const LinuxDevice>(m_device);
QTC_ASSERT(linuxDevice, return {});
const FilePath sshBinary = SshSettings::sshFilePath(); const FilePath sshBinary = SshSettings::sshFilePath();
const bool useTerminal = q->m_setup.m_terminalMode != TerminalMode::Off || q->m_setup.m_ptyData; const bool useTerminal = q->m_setup.m_terminalMode != TerminalMode::Off || q->m_setup.m_ptyData;
const bool usePidMarker = !useTerminal; const bool usePidMarker = !useTerminal;
const bool sourceProfile = m_device->extraData(Constants::SourceProfile).toBool(); const bool sourceProfile = linuxDevice->sourceProfile();
const bool useX = !m_sshParameters.x11DisplayName().isEmpty(); const bool useX = !m_sshParameters.x11DisplayName().isEmpty();
CommandLine cmd{sshBinary}; CommandLine cmd{sshBinary};
@@ -1029,7 +1032,13 @@ LinuxDevice::LinuxDevice()
setFreePorts(PortList::fromString(QLatin1String("10000-10100"))); setFreePorts(PortList::fromString(QLatin1String("10000-10100")));
SshParameters sshParams; SshParameters sshParams;
sshParams.setTimeout(10); sshParams.setTimeout(10);
setSshParameters(sshParams); setDefaultSshParameters(sshParams);
sourceProfile.setSettingsKey("SourceProfile");
sourceProfile.setDefaultValue(true);
sourceProfile.setToolTip(Tr::tr("Source profile before executing commands"));
sourceProfile.setLabelText(Tr::tr("Source %1 and %2").arg("/etc/profile").arg("$HOME/.profile"));
sourceProfile.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBox);
addDeviceAction({Tr::tr("Deploy Public Key..."), [](const IDevice::Ptr &device) { addDeviceAction({Tr::tr("Deploy Public Key..."), [](const IDevice::Ptr &device) {
if (auto d = Internal::PublicKeyDeploymentDialog::createDialog(device)) { if (auto d = Internal::PublicKeyDeploymentDialog::createDialog(device)) {
@@ -1368,6 +1377,35 @@ void Internal::LinuxDeviceFactory::shutdownExistingDevices()
} }
}); });
} }
namespace {
static const char SourceProfile[] = "RemoteLinux.SourceProfile";
static void backwardsFromExtraData(LinuxDevice *device)
{
QVariant sourceProfile = device->extraData(SourceProfile);
if (sourceProfile.isValid())
device->sourceProfile.setValue(sourceProfile.toBool());
}
static void backwardsToExtraData(LinuxDevice *device)
{
device->setExtraData(SourceProfile, device->sourceProfile.value());
}
} // namespace
void LinuxDevice::fromMap(const Utils::Store &map)
{
ProjectExplorer::IDevice::fromMap(map);
backwardsFromExtraData(this);
}
void LinuxDevice::toMap(Utils::Store &map) const
{
backwardsToExtraData(const_cast<LinuxDevice *>(this));
ProjectExplorer::IDevice::toMap(map);
}
} // namespace RemoteLinux } // namespace RemoteLinux
#include "linuxdevice.moc" #include "linuxdevice.moc"

View File

@@ -76,6 +76,12 @@ public:
void shutdown(); void shutdown();
void fromMap(const Utils::Store &map) override;
void toMap(Utils::Store &map) const override;
public:
Utils::BoolAspect sourceProfile{this};
protected: protected:
LinuxDevice(); LinuxDevice();

View File

@@ -19,8 +19,6 @@ const char GenericDeployStepId[] = "RemoteLinux.RsyncDeployStep";
const char CustomCommandDeployStepId[] = "RemoteLinux.GenericRemoteLinuxCustomCommandDeploymentStep"; const char CustomCommandDeployStepId[] = "RemoteLinux.GenericRemoteLinuxCustomCommandDeploymentStep";
const char KillAppStepId[] = "RemoteLinux.KillAppStep"; const char KillAppStepId[] = "RemoteLinux.KillAppStep";
const char SourceProfile[] = "RemoteLinux.SourceProfile";
const char LinkDevice[] = "RemoteLinux.LinkDevice";
const char SshForwardPort[] = "RemoteLinux.SshForwardPort"; const char SshForwardPort[] = "RemoteLinux.SshForwardPort";
const char DisableSharing[] = "RemoteLinux.DisableSharing"; const char DisableSharing[] = "RemoteLinux.DisableSharing";

View File

@@ -121,7 +121,7 @@ private:
void start() final void start() final
{ {
m_sshParameters = displayless(m_device.sshParameters()); m_sshParameters = displayless(m_device.sshParameters());
const Id linkDeviceId = Id::fromSetting(m_device.extraData(Constants::LinkDevice)); const Id linkDeviceId = m_device.linkDeviceId();
const auto linkDevice = DeviceManager::instance()->find(linkDeviceId); const auto linkDevice = DeviceManager::instance()->find(linkDeviceId);
const bool useConnectionSharing = !linkDevice && SshSettings::connectionSharingEnabled(); const bool useConnectionSharing = !linkDevice && SshSettings::connectionSharingEnabled();
@@ -186,7 +186,7 @@ private:
FilePath sftpBinary = SshSettings::sftpFilePath(); FilePath sftpBinary = SshSettings::sftpFilePath();
// This is a hack. We only test the last hop here. // This is a hack. We only test the last hop here.
const Id linkDeviceId = Id::fromSetting(device().extraData(Constants::LinkDevice)); const Id linkDeviceId = device().linkDeviceId();
if (const auto linkDevice = DeviceManager::instance()->find(linkDeviceId)) if (const auto linkDevice = DeviceManager::instance()->find(linkDeviceId))
sftpBinary = linkDevice->filePath(sftpBinary.fileName()).searchInPath(); sftpBinary = linkDevice->filePath(sftpBinary.fileName()).searchInPath();