forked from qt-creator/qt-creator
LinuxDevice: Add disconnected state
Change-Id: Ic2c909e3375a9fb025a335c1ca65621fa031d000 Reviewed-by: Christian Kandeler <christian.kandeler@qt.io> Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
This commit is contained in:
@@ -878,52 +878,73 @@ QByteArray DesktopDeviceFileAccess::fileId(const FilePath &filePath) const
|
||||
|
||||
// UnixDeviceAccess
|
||||
|
||||
static Utils::unexpected<QString> make_unexpected_disconnected()
|
||||
{
|
||||
return make_unexpected(Tr::tr("Device is not connected"));
|
||||
}
|
||||
|
||||
UnixDeviceFileAccess::~UnixDeviceFileAccess() = default;
|
||||
|
||||
bool UnixDeviceFileAccess::runInShellSuccess(const CommandLine &cmdLine,
|
||||
const QByteArray &stdInData) const
|
||||
{
|
||||
if (disconnected())
|
||||
return false;
|
||||
return runInShell(cmdLine, stdInData).exitCode == 0;
|
||||
}
|
||||
|
||||
bool UnixDeviceFileAccess::isExecutableFile(const FilePath &filePath) const
|
||||
{
|
||||
if (disconnected())
|
||||
return false;
|
||||
const QString path = filePath.path();
|
||||
return runInShellSuccess({"test", {"-x", path}, OsType::OsTypeLinux});
|
||||
}
|
||||
|
||||
bool UnixDeviceFileAccess::isReadableFile(const FilePath &filePath) const
|
||||
{
|
||||
if (disconnected())
|
||||
return false;
|
||||
const QString path = filePath.path();
|
||||
return runInShellSuccess({"test", {"-r", path, "-a", "-f", path}, OsType::OsTypeLinux});
|
||||
}
|
||||
|
||||
bool UnixDeviceFileAccess::isWritableFile(const FilePath &filePath) const
|
||||
{
|
||||
if (disconnected())
|
||||
return false;
|
||||
const QString path = filePath.path();
|
||||
return runInShellSuccess({"test", {"-w", path, "-a", "-f", path}, OsType::OsTypeLinux});
|
||||
}
|
||||
|
||||
bool UnixDeviceFileAccess::isReadableDirectory(const FilePath &filePath) const
|
||||
{
|
||||
if (disconnected())
|
||||
return false;
|
||||
const QString path = filePath.path();
|
||||
return runInShellSuccess({"test", {"-r", path, "-a", "-d", path}, OsType::OsTypeLinux});
|
||||
}
|
||||
|
||||
bool UnixDeviceFileAccess::isWritableDirectory(const FilePath &filePath) const
|
||||
{
|
||||
if (disconnected())
|
||||
return false;
|
||||
const QString path = filePath.path();
|
||||
return runInShellSuccess({"test", {"-w", path, "-a", "-d", path}, OsType::OsTypeLinux});
|
||||
}
|
||||
|
||||
bool UnixDeviceFileAccess::isFile(const FilePath &filePath) const
|
||||
{
|
||||
if (disconnected())
|
||||
return false;
|
||||
const QString path = filePath.path();
|
||||
return runInShellSuccess({"test", {"-f", path}, OsType::OsTypeLinux});
|
||||
}
|
||||
|
||||
bool UnixDeviceFileAccess::isDirectory(const FilePath &filePath) const
|
||||
{
|
||||
if (disconnected())
|
||||
return false;
|
||||
if (filePath.isRootPath())
|
||||
return true;
|
||||
|
||||
@@ -933,12 +954,16 @@ bool UnixDeviceFileAccess::isDirectory(const FilePath &filePath) const
|
||||
|
||||
bool UnixDeviceFileAccess::isSymLink(const FilePath &filePath) const
|
||||
{
|
||||
if (disconnected())
|
||||
return false;
|
||||
const QString path = filePath.path();
|
||||
return runInShellSuccess({"test", {"-h", path}, OsType::OsTypeLinux});
|
||||
}
|
||||
|
||||
bool UnixDeviceFileAccess::hasHardLinks(const FilePath &filePath) const
|
||||
{
|
||||
if (disconnected())
|
||||
return false;
|
||||
const QStringList args = statArgs(filePath, "%h", "%l");
|
||||
const RunResult result = runInShell({"stat", args, OsType::OsTypeLinux});
|
||||
return result.stdOut.toLongLong() > 1;
|
||||
@@ -946,29 +971,39 @@ bool UnixDeviceFileAccess::hasHardLinks(const FilePath &filePath) const
|
||||
|
||||
bool UnixDeviceFileAccess::ensureExistingFile(const FilePath &filePath) const
|
||||
{
|
||||
if (disconnected())
|
||||
return false;
|
||||
const QString path = filePath.path();
|
||||
return runInShellSuccess({"touch", {path}, OsType::OsTypeLinux});
|
||||
}
|
||||
|
||||
bool UnixDeviceFileAccess::createDirectory(const FilePath &filePath) const
|
||||
{
|
||||
if (disconnected())
|
||||
return false;
|
||||
const QString path = filePath.path();
|
||||
return runInShellSuccess({"mkdir", {"-p", path}, OsType::OsTypeLinux});
|
||||
}
|
||||
|
||||
bool UnixDeviceFileAccess::exists(const FilePath &filePath) const
|
||||
{
|
||||
if (disconnected())
|
||||
return false;
|
||||
const QString path = filePath.path();
|
||||
return runInShellSuccess({"test", {"-e", path}, OsType::OsTypeLinux});
|
||||
}
|
||||
|
||||
bool UnixDeviceFileAccess::removeFile(const FilePath &filePath) const
|
||||
{
|
||||
if (disconnected())
|
||||
return false;
|
||||
return runInShellSuccess({"rm", {filePath.path()}, OsType::OsTypeLinux});
|
||||
}
|
||||
|
||||
bool UnixDeviceFileAccess::removeRecursively(const FilePath &filePath, QString *error) const
|
||||
{
|
||||
if (disconnected())
|
||||
return false;
|
||||
QTC_ASSERT(filePath.path().startsWith('/'), return false);
|
||||
|
||||
const QString path = filePath.cleanPath().path();
|
||||
@@ -989,6 +1024,8 @@ bool UnixDeviceFileAccess::removeRecursively(const FilePath &filePath, QString *
|
||||
expected_str<void> UnixDeviceFileAccess::copyFile(const FilePath &filePath,
|
||||
const FilePath &target) const
|
||||
{
|
||||
if (disconnected())
|
||||
return make_unexpected_disconnected();
|
||||
const RunResult result = runInShell(
|
||||
{"cp", {filePath.path(), target.path()}, OsType::OsTypeLinux});
|
||||
|
||||
@@ -1003,11 +1040,17 @@ expected_str<void> UnixDeviceFileAccess::copyFile(const FilePath &filePath,
|
||||
|
||||
bool UnixDeviceFileAccess::renameFile(const FilePath &filePath, const FilePath &target) const
|
||||
{
|
||||
if (disconnected())
|
||||
return false;
|
||||
|
||||
return runInShellSuccess({"mv", {filePath.path(), target.path()}, OsType::OsTypeLinux});
|
||||
}
|
||||
|
||||
FilePath UnixDeviceFileAccess::symLinkTarget(const FilePath &filePath) const
|
||||
{
|
||||
if (disconnected())
|
||||
return {};
|
||||
|
||||
const RunResult result = runInShell(
|
||||
{"readlink", {"-n", "-e", filePath.path()}, OsType::OsTypeLinux});
|
||||
const QString out = QString::fromUtf8(result.stdOut);
|
||||
@@ -1018,6 +1061,9 @@ expected_str<QByteArray> UnixDeviceFileAccess::fileContents(const FilePath &file
|
||||
qint64 limit,
|
||||
qint64 offset) const
|
||||
{
|
||||
if (disconnected())
|
||||
return make_unexpected_disconnected();
|
||||
|
||||
QStringList args = {"if=" + filePath.path()};
|
||||
if (limit > 0 || offset > 0) {
|
||||
const qint64 gcd = std::gcd(limit, offset);
|
||||
@@ -1045,6 +1091,9 @@ expected_str<qint64> UnixDeviceFileAccess::writeFileContents(const FilePath &fil
|
||||
const QByteArray &data,
|
||||
qint64 offset) const
|
||||
{
|
||||
if (disconnected())
|
||||
return make_unexpected_disconnected();
|
||||
|
||||
QStringList args = {"of=" + filePath.path()};
|
||||
if (offset != 0) {
|
||||
args.append("bs=1");
|
||||
@@ -1061,6 +1110,9 @@ expected_str<qint64> UnixDeviceFileAccess::writeFileContents(const FilePath &fil
|
||||
|
||||
expected_str<FilePath> UnixDeviceFileAccess::createTempFile(const FilePath &filePath)
|
||||
{
|
||||
if (disconnected())
|
||||
return make_unexpected_disconnected();
|
||||
|
||||
if (!m_hasMkTemp.has_value())
|
||||
m_hasMkTemp = runInShellSuccess({"which", {"mktemp"}, OsType::OsTypeLinux});
|
||||
|
||||
@@ -1120,6 +1172,9 @@ expected_str<FilePath> UnixDeviceFileAccess::createTempFile(const FilePath &file
|
||||
|
||||
QDateTime UnixDeviceFileAccess::lastModified(const FilePath &filePath) const
|
||||
{
|
||||
if (disconnected())
|
||||
return {};
|
||||
|
||||
const RunResult result = runInShell(
|
||||
{"stat", {"-L", "-c", "%Y", filePath.path()}, OsType::OsTypeLinux});
|
||||
qint64 secs = result.stdOut.toLongLong();
|
||||
@@ -1131,6 +1186,9 @@ QStringList UnixDeviceFileAccess::statArgs(const FilePath &filePath,
|
||||
const QString &linuxFormat,
|
||||
const QString &macFormat) const
|
||||
{
|
||||
if (disconnected())
|
||||
return {};
|
||||
|
||||
return (filePath.osType() == OsTypeMac ? QStringList{"-f", macFormat}
|
||||
: QStringList{"-c", linuxFormat})
|
||||
<< "-L" << filePath.path();
|
||||
@@ -1138,6 +1196,9 @@ QStringList UnixDeviceFileAccess::statArgs(const FilePath &filePath,
|
||||
|
||||
QFile::Permissions UnixDeviceFileAccess::permissions(const FilePath &filePath) const
|
||||
{
|
||||
if (disconnected())
|
||||
return {};
|
||||
|
||||
QStringList args = statArgs(filePath, "%a", "%p");
|
||||
|
||||
const RunResult result = runInShell({"stat", args, OsType::OsTypeLinux});
|
||||
@@ -1161,6 +1222,9 @@ QFile::Permissions UnixDeviceFileAccess::permissions(const FilePath &filePath) c
|
||||
|
||||
bool UnixDeviceFileAccess::setPermissions(const FilePath &filePath, QFile::Permissions perms) const
|
||||
{
|
||||
if (disconnected())
|
||||
return false;
|
||||
|
||||
const int flags = int(perms);
|
||||
return runInShellSuccess(
|
||||
{"chmod", {QString::number(flags, 16), filePath.path()}, OsType::OsTypeLinux});
|
||||
@@ -1168,6 +1232,9 @@ bool UnixDeviceFileAccess::setPermissions(const FilePath &filePath, QFile::Permi
|
||||
|
||||
qint64 UnixDeviceFileAccess::fileSize(const FilePath &filePath) const
|
||||
{
|
||||
if (disconnected())
|
||||
return -1;
|
||||
|
||||
const QStringList args = statArgs(filePath, "%s", "%z");
|
||||
const RunResult result = runInShell({"stat", args, OsType::OsTypeLinux});
|
||||
return result.stdOut.toLongLong();
|
||||
@@ -1175,12 +1242,18 @@ qint64 UnixDeviceFileAccess::fileSize(const FilePath &filePath) const
|
||||
|
||||
qint64 UnixDeviceFileAccess::bytesAvailable(const FilePath &filePath) const
|
||||
{
|
||||
if (disconnected())
|
||||
return -1;
|
||||
|
||||
const RunResult result = runInShell({"df", {"-k", filePath.path()}, OsType::OsTypeLinux});
|
||||
return FileUtils::bytesAvailableFromDFOutput(result.stdOut);
|
||||
}
|
||||
|
||||
QByteArray UnixDeviceFileAccess::fileId(const FilePath &filePath) const
|
||||
{
|
||||
if (disconnected())
|
||||
return {};
|
||||
|
||||
const QStringList args = statArgs(filePath, "%D:%i", "%d:%i");
|
||||
|
||||
const RunResult result = runInShell({"stat", args, OsType::OsTypeLinux});
|
||||
@@ -1192,6 +1265,9 @@ QByteArray UnixDeviceFileAccess::fileId(const FilePath &filePath) const
|
||||
|
||||
FilePathInfo UnixDeviceFileAccess::filePathInfo(const FilePath &filePath) const
|
||||
{
|
||||
if (disconnected())
|
||||
return {};
|
||||
|
||||
if (filePath.path() == "/") // TODO: Add FilePath::isRoot()
|
||||
{
|
||||
const FilePathInfo r{4096,
|
||||
@@ -1218,6 +1294,9 @@ bool UnixDeviceFileAccess::iterateWithFind(const FilePath &filePath,
|
||||
const FileFilter &filter,
|
||||
const FilePath::IterateDirCallback &callBack) const
|
||||
{
|
||||
if (disconnected())
|
||||
return false;
|
||||
|
||||
QTC_CHECK(filePath.isAbsolutePath());
|
||||
|
||||
CommandLine cmdLine{"find", filter.asFindArguments(filePath.path()), OsType::OsTypeLinux};
|
||||
@@ -1290,6 +1369,9 @@ void UnixDeviceFileAccess::findUsingLs(const QString ¤t,
|
||||
QStringList *found,
|
||||
const QString &start) const
|
||||
{
|
||||
if (disconnected())
|
||||
return;
|
||||
|
||||
const RunResult result = runInShell(
|
||||
{"ls", {"-1", "-a", "-p", "--", current}, OsType::OsTypeLinux});
|
||||
const QStringList entries = QString::fromUtf8(result.stdOut).split('\n', Qt::SkipEmptyParts);
|
||||
@@ -1349,6 +1431,9 @@ void UnixDeviceFileAccess::iterateDirectory(const FilePath &filePath,
|
||||
const FilePath::IterateDirCallback &callBack,
|
||||
const FileFilter &filter) const
|
||||
{
|
||||
if (disconnected())
|
||||
return;
|
||||
|
||||
// We try to use 'find' first, because that can filter better directly.
|
||||
// Unfortunately, it's not installed on all devices by default.
|
||||
if (m_tryUseFind) {
|
||||
@@ -1366,9 +1451,17 @@ void UnixDeviceFileAccess::iterateDirectory(const FilePath &filePath,
|
||||
|
||||
Environment UnixDeviceFileAccess::deviceEnvironment() const
|
||||
{
|
||||
if (disconnected())
|
||||
return {};
|
||||
|
||||
const RunResult result = runInShell({"env", {}, OsType::OsTypeLinux});
|
||||
const QString out = QString::fromUtf8(result.stdOut);
|
||||
return Environment(out.split('\n', Qt::SkipEmptyParts), OsTypeLinux);
|
||||
}
|
||||
|
||||
bool UnixDeviceFileAccess::disconnected() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace Utils
|
||||
|
@@ -188,6 +188,8 @@ protected:
|
||||
QStringList *found,
|
||||
const QString &start) const;
|
||||
|
||||
virtual bool disconnected() const;
|
||||
|
||||
private:
|
||||
bool iterateWithFind(const FilePath &filePath,
|
||||
const FileFilter &filter,
|
||||
|
@@ -312,6 +312,9 @@ void DeviceSettingsWidget::testDevice()
|
||||
dlg->setAttribute(Qt::WA_DeleteOnClose);
|
||||
dlg->setModal(true);
|
||||
dlg->show();
|
||||
connect(dlg, &QObject::destroyed, this, [this, id = device->id()] {
|
||||
handleDeviceUpdated(id);
|
||||
});
|
||||
}
|
||||
|
||||
void DeviceSettingsWidget::handleDeviceUpdated(Id id)
|
||||
|
@@ -593,7 +593,7 @@ QString IDevice::deviceStateToString() const
|
||||
|
||||
QPixmap IDevice::deviceStateIcon() const
|
||||
{
|
||||
switch (d->deviceState) {
|
||||
switch (deviceState()) {
|
||||
case IDevice::DeviceReadyToUse: return Icons::DEVICE_READY_INDICATOR.pixmap();
|
||||
case IDevice::DeviceConnected: return Icons::DEVICE_CONNECTED_INDICATOR.pixmap();
|
||||
case IDevice::DeviceDisconnected: return Icons::DEVICE_DISCONNECTED_INDICATOR.pixmap();
|
||||
|
@@ -105,7 +105,7 @@ public:
|
||||
|
||||
virtual ~IDevice();
|
||||
|
||||
Ptr clone() const;
|
||||
virtual Ptr clone() const;
|
||||
|
||||
DeviceSettings *settings() const;
|
||||
|
||||
@@ -154,9 +154,9 @@ public:
|
||||
virtual DeviceProcessSignalOperation::Ptr signalOperation() const;
|
||||
|
||||
enum DeviceState { DeviceReadyToUse, DeviceConnected, DeviceDisconnected, DeviceStateUnknown };
|
||||
DeviceState deviceState() const;
|
||||
virtual DeviceState deviceState() const;
|
||||
void setDeviceState(const DeviceState state);
|
||||
QString deviceStateToString() const;
|
||||
virtual QString deviceStateToString() const;
|
||||
QPixmap deviceStateIcon() const;
|
||||
|
||||
static Utils::Id typeFromMap(const Utils::Store &map);
|
||||
|
@@ -21,12 +21,15 @@
|
||||
#include <projectexplorer/devicesupport/processlist.h>
|
||||
#include <projectexplorer/devicesupport/sshparameters.h>
|
||||
#include <projectexplorer/devicesupport/sshsettings.h>
|
||||
#include <projectexplorer/projectexplorerconstants.h>
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/async.h>
|
||||
#include <utils/devicefileaccess.h>
|
||||
#include <utils/deviceshell.h>
|
||||
#include <utils/environment.h>
|
||||
#include <utils/hostosinfo.h>
|
||||
#include <utils/infobar.h>
|
||||
#include <utils/port.h>
|
||||
#include <utils/portlist.h>
|
||||
#include <utils/process.h>
|
||||
@@ -292,6 +295,8 @@ public:
|
||||
|
||||
Environment deviceEnvironment() const override;
|
||||
|
||||
bool disconnected() const override;
|
||||
|
||||
LinuxDevicePrivate *m_dev;
|
||||
};
|
||||
|
||||
@@ -307,7 +312,7 @@ public:
|
||||
explicit LinuxDevicePrivate(LinuxDevice *parent);
|
||||
~LinuxDevicePrivate();
|
||||
|
||||
bool setupShell();
|
||||
bool setupShell(const SshParameters &sshParameters);
|
||||
RunResult runInShell(const CommandLine &cmd, const QByteArray &stdInData = {});
|
||||
|
||||
void attachToSharedConnection(SshConnectionHandle *connectionHandle,
|
||||
@@ -319,6 +324,10 @@ public:
|
||||
void checkOsType();
|
||||
void queryOsType(std::function<RunResult(const CommandLine &)> run);
|
||||
|
||||
void setDisconnected(bool disconnected);
|
||||
bool disconnected() const;
|
||||
bool checkDisconnectedWithWarning();
|
||||
|
||||
LinuxDevice *q = nullptr;
|
||||
QThread m_shellThread;
|
||||
ShellThreadHandler *m_handler = nullptr;
|
||||
@@ -327,6 +336,7 @@ public:
|
||||
|
||||
QReadWriteLock m_environmentCacheLock;
|
||||
std::optional<Environment> m_environmentCache;
|
||||
bool m_disconnected = false;
|
||||
};
|
||||
|
||||
void LinuxDevicePrivate::invalidateEnvironmentCache()
|
||||
@@ -358,14 +368,24 @@ Environment LinuxDevicePrivate::getEnvironment()
|
||||
RunResult LinuxDeviceFileAccess::runInShell(const CommandLine &cmdLine,
|
||||
const QByteArray &stdInData) const
|
||||
{
|
||||
if (disconnected())
|
||||
return {-1, {}, Tr::tr("Device is disconnected.").toUtf8()};
|
||||
return m_dev->runInShell(cmdLine, stdInData);
|
||||
}
|
||||
|
||||
Environment LinuxDeviceFileAccess::deviceEnvironment() const
|
||||
{
|
||||
if (disconnected())
|
||||
return {};
|
||||
|
||||
return m_dev->getEnvironment();
|
||||
}
|
||||
|
||||
bool LinuxDeviceFileAccess::disconnected() const
|
||||
{
|
||||
return m_dev->checkDisconnectedWithWarning();
|
||||
}
|
||||
|
||||
// SshProcessImpl
|
||||
|
||||
class SshProcessInterfacePrivate : public QObject
|
||||
@@ -1023,6 +1043,15 @@ LinuxDevice::~LinuxDevice()
|
||||
delete d;
|
||||
}
|
||||
|
||||
IDevice::Ptr LinuxDevice::clone() const
|
||||
{
|
||||
IDevice::Ptr clone = IDevice::clone();
|
||||
Ptr linuxClone = std::dynamic_pointer_cast<LinuxDevice>(clone);
|
||||
QTC_ASSERT(linuxClone, return clone);
|
||||
linuxClone->d->setDisconnected(d->disconnected());
|
||||
return clone;
|
||||
}
|
||||
|
||||
IDeviceWidget *LinuxDevice::createWidget()
|
||||
{
|
||||
return new Internal::GenericLinuxDeviceConfigurationWidget(shared_from_this());
|
||||
@@ -1106,15 +1135,31 @@ void LinuxDevicePrivate::queryOsType(std::function<RunResult(const CommandLine &
|
||||
q->_setOsType(OsTypeLinux);
|
||||
}
|
||||
|
||||
void LinuxDevicePrivate::setDisconnected(bool disconnected)
|
||||
{
|
||||
if (disconnected == m_disconnected)
|
||||
return;
|
||||
|
||||
m_disconnected = disconnected;
|
||||
|
||||
if (m_disconnected)
|
||||
m_handler->closeShell();
|
||||
|
||||
}
|
||||
|
||||
bool LinuxDevicePrivate::disconnected() const
|
||||
{
|
||||
return m_disconnected;
|
||||
}
|
||||
|
||||
void LinuxDevicePrivate::checkOsType()
|
||||
{
|
||||
queryOsType([this](const CommandLine &cmd) { return runInShell(cmd); });
|
||||
}
|
||||
|
||||
// Call me with shell mutex locked
|
||||
bool LinuxDevicePrivate::setupShell()
|
||||
bool LinuxDevicePrivate::setupShell(const SshParameters &sshParameters)
|
||||
{
|
||||
const SshParameters sshParameters = q->sshParameters();
|
||||
if (m_handler->isRunning(sshParameters))
|
||||
return true;
|
||||
|
||||
@@ -1126,8 +1171,12 @@ bool LinuxDevicePrivate::setupShell()
|
||||
}, Qt::BlockingQueuedConnection, &ok);
|
||||
|
||||
if (ok) {
|
||||
setDisconnected(false);
|
||||
queryOsType([this](const CommandLine &cmd) { return m_handler->runInShell(cmd); });
|
||||
} else {
|
||||
setDisconnected(true);
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
@@ -1135,11 +1184,43 @@ RunResult LinuxDevicePrivate::runInShell(const CommandLine &cmd, const QByteArra
|
||||
{
|
||||
QMutexLocker locker(&m_shellMutex);
|
||||
DEBUG(cmd.toUserOutput());
|
||||
const bool isSetup = setupShell();
|
||||
if (checkDisconnectedWithWarning())
|
||||
return {};
|
||||
const bool isSetup = setupShell(q->sshParameters());
|
||||
if (checkDisconnectedWithWarning())
|
||||
return {};
|
||||
QTC_ASSERT(isSetup, return {});
|
||||
return m_handler->runInShell(cmd, data);
|
||||
}
|
||||
|
||||
bool LinuxDevicePrivate::checkDisconnectedWithWarning()
|
||||
{
|
||||
if (!disconnected())
|
||||
return false;
|
||||
|
||||
QMetaObject::invokeMethod(Core::ICore::infoBar(), [id = q->id(), name = q->displayName()] {
|
||||
if (!Core::ICore::infoBar()->canInfoBeAdded(id))
|
||||
return;
|
||||
const QString warnStr
|
||||
= Tr::tr("Device \"%1\" is currently marked as disconnected.").arg(name);
|
||||
InfoBarEntry info(id, warnStr, InfoBarEntry::GlobalSuppression::Enabled);
|
||||
info.setDetailsWidgetCreator([] {
|
||||
const auto label = new QLabel(Tr::tr(
|
||||
"The device was not available when trying to connect previously.<br>"
|
||||
"No further connection attempts will be made until the device is manually reset "
|
||||
"by running a successful connection test via the "
|
||||
"<a href=\"dummy\">settings page</a>."));
|
||||
label->setWordWrap(true);
|
||||
QObject::connect(label, &QLabel::linkActivated, [] {
|
||||
Core::ICore::showOptionsDialog(ProjectExplorer::Constants::DEVICE_SETTINGS_PAGE_ID);
|
||||
});
|
||||
return label;
|
||||
});
|
||||
Core::ICore::infoBar()->addInfo(info);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
void LinuxDevicePrivate::attachToSharedConnection(SshConnectionHandle *connectionHandle,
|
||||
const SshParameters &sshParameters)
|
||||
{
|
||||
@@ -1522,6 +1603,37 @@ void LinuxDevice::checkOsType()
|
||||
d->checkOsType();
|
||||
}
|
||||
|
||||
IDevice::DeviceState LinuxDevice::deviceState() const
|
||||
{
|
||||
if (isDisconnected())
|
||||
return DeviceDisconnected;
|
||||
return IDevice::deviceState();
|
||||
}
|
||||
|
||||
QString LinuxDevice::deviceStateToString() const
|
||||
{
|
||||
if (isDisconnected())
|
||||
return Tr::tr("Device is considered unconnected. Re-run device test to reset state.");
|
||||
return IDevice::deviceStateToString();
|
||||
}
|
||||
|
||||
bool LinuxDevice::isDisconnected() const
|
||||
{
|
||||
return d->disconnected();
|
||||
}
|
||||
void LinuxDevice::setDisconnected(bool disconnected)
|
||||
{
|
||||
d->setDisconnected(disconnected);
|
||||
}
|
||||
|
||||
QFuture<bool> LinuxDevice::tryToConnect()
|
||||
{
|
||||
return Utils::asyncRun([this] {
|
||||
QMutexLocker locker(&d->m_shellMutex);
|
||||
return d->setupShell(sshParameters());
|
||||
});
|
||||
}
|
||||
|
||||
namespace Internal {
|
||||
|
||||
class LinuxDeviceFactory final : public IDeviceFactory
|
||||
|
@@ -20,6 +20,8 @@ public:
|
||||
|
||||
static Ptr create() { return Ptr(new LinuxDevice); }
|
||||
|
||||
IDevice::Ptr clone() const override;
|
||||
|
||||
ProjectExplorer::IDeviceWidget *createWidget() override;
|
||||
|
||||
bool canCreateProcessModel() const override { return true; }
|
||||
@@ -42,6 +44,14 @@ public:
|
||||
class LinuxDevicePrivate *connectionAccess() const;
|
||||
void checkOsType() override;
|
||||
|
||||
DeviceState deviceState() const override;
|
||||
QString deviceStateToString() const override;
|
||||
|
||||
bool isDisconnected() const;
|
||||
void setDisconnected(bool disconnected);
|
||||
|
||||
QFuture<bool> tryToConnect();
|
||||
|
||||
protected:
|
||||
LinuxDevice();
|
||||
|
||||
|
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "linuxdevicetester.h"
|
||||
|
||||
#include "linuxdevice.h"
|
||||
#include "remotelinuxtr.h"
|
||||
|
||||
#include <projectexplorer/devicesupport/deviceusedportsgatherer.h>
|
||||
@@ -17,6 +18,8 @@
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/stringutils.h>
|
||||
|
||||
#include <QFutureWatcher>
|
||||
|
||||
using namespace ProjectExplorer;
|
||||
using namespace Tasking;
|
||||
using namespace Utils;
|
||||
@@ -43,9 +46,13 @@ public:
|
||||
const Storage<TransferStorage> &storage) const;
|
||||
GroupItem transferTasks() const;
|
||||
GroupItem commandTasks() const;
|
||||
void runCommandTests();
|
||||
|
||||
bool isRunning() const { return m_connectionTest || m_taskTreeRunner.isRunning(); }
|
||||
|
||||
GenericLinuxDeviceTester *q = nullptr;
|
||||
IDevice::Ptr m_device;
|
||||
LinuxDevice::Ptr m_device;
|
||||
QFutureWatcher<bool> *m_connectionTest = nullptr;
|
||||
TaskTreeRunner m_taskTreeRunner;
|
||||
QStringList m_extraCommands;
|
||||
QList<GroupItem> m_extraTests;
|
||||
@@ -276,6 +283,20 @@ GroupItem GenericLinuxDeviceTesterPrivate::commandTasks() const
|
||||
return root;
|
||||
}
|
||||
|
||||
void GenericLinuxDeviceTesterPrivate::runCommandTests()
|
||||
{
|
||||
const Group root {
|
||||
echoTask("Hello"), // No quoting necessary
|
||||
echoTask("Hello Remote World!"), // Checks quoting, too.
|
||||
unameTask(),
|
||||
gathererTask(),
|
||||
transferTasks(),
|
||||
m_extraTests,
|
||||
commandTasks()
|
||||
};
|
||||
m_taskTreeRunner.start(root);
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
using namespace Internal;
|
||||
@@ -302,26 +323,40 @@ void GenericLinuxDeviceTester::setExtraTests(const QList<GroupItem> &extraTests)
|
||||
|
||||
void GenericLinuxDeviceTester::testDevice(const IDevice::Ptr &deviceConfiguration)
|
||||
{
|
||||
QTC_ASSERT(!d->m_taskTreeRunner.isRunning(), return);
|
||||
QTC_ASSERT(!d->isRunning(), return);
|
||||
|
||||
d->m_device = deviceConfiguration;
|
||||
emit progressMessage(Tr::tr("Connecting to device..."));
|
||||
|
||||
const Group root {
|
||||
d->echoTask("Hello"), // No quoting necessary
|
||||
d->echoTask("Hello Remote World!"), // Checks quoting, too.
|
||||
d->unameTask(),
|
||||
d->gathererTask(),
|
||||
d->transferTasks(),
|
||||
d->m_extraTests,
|
||||
d->commandTasks()
|
||||
};
|
||||
d->m_taskTreeRunner.start(root);
|
||||
d->m_device = std::static_pointer_cast<LinuxDevice>(deviceConfiguration);
|
||||
|
||||
d->m_connectionTest = new QFutureWatcher<bool>(this);
|
||||
d->m_connectionTest->setFuture(d->m_device->tryToConnect());
|
||||
|
||||
connect(d->m_connectionTest, &QFutureWatcher<bool>::finished, this, [this] {
|
||||
const bool success = d->m_connectionTest->result();
|
||||
d->m_connectionTest->deleteLater();
|
||||
d->m_connectionTest = nullptr;
|
||||
if (success) {
|
||||
emit progressMessage(Tr::tr("Connected. Now doing extended checks.\n"));
|
||||
d->runCommandTests();
|
||||
} else {
|
||||
emit errorMessage(
|
||||
Tr::tr("Basic connectivity test failed, device is considered unusable."));
|
||||
emit finished(TestFailure);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void GenericLinuxDeviceTester::stopTest()
|
||||
{
|
||||
QTC_ASSERT(d->m_taskTreeRunner.isRunning(), return);
|
||||
QTC_ASSERT(d->isRunning(), return);
|
||||
if (d->m_connectionTest) {
|
||||
d->m_connectionTest->disconnect();
|
||||
d->m_connectionTest->cancel();
|
||||
d->m_connectionTest = nullptr;
|
||||
} else {
|
||||
d->m_taskTreeRunner.reset();
|
||||
}
|
||||
emit finished(TestFailure);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user