forked from qt-creator/qt-creator
RemoteLinux: Implement some of the remote file API
Implementation of remote file API for correct FilePath work with RemoteLinux. Added tests for this functionality Run ssh shell in separate thread. The linux device instance keeps its own thread for running SshRemoteProcess. In this way all calls to filepath interface of linux device coming from different threads are executed in one thread (SshRemoteProcess is reentrant, but not thread safe). The redirection to the device thread is done by invoking SshRemoteProcess' methods through BlockingQueuedConnection. Done-by: Artem Sokolovskii Change-Id: Id8756738d3a4597f175c8ef000c148d0c8536eeb Reviewed-by: Jarek Kobus <jaroslaw.kobus@qt.io>
This commit is contained in:
@@ -66,6 +66,46 @@ SshConnectionParameters::SshConnectionParameters()
|
|||||||
url.setPort(0);
|
url.setPort(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QStringList SshConnectionParameters::connectionOptions(const FilePath &binary) const
|
||||||
|
{
|
||||||
|
QString hostKeyCheckingString;
|
||||||
|
switch (hostKeyCheckingMode) {
|
||||||
|
case SshHostKeyCheckingNone:
|
||||||
|
case SshHostKeyCheckingAllowNoMatch:
|
||||||
|
// There is "accept-new" as well, but only since 7.6.
|
||||||
|
hostKeyCheckingString = "no";
|
||||||
|
break;
|
||||||
|
case SshHostKeyCheckingStrict:
|
||||||
|
hostKeyCheckingString = "yes";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList args{"-o", "StrictHostKeyChecking=" + hostKeyCheckingString,
|
||||||
|
"-o", "Port=" + QString::number(port())};
|
||||||
|
|
||||||
|
if (!userName().isEmpty())
|
||||||
|
args.append({"-o", "User=" + userName()});
|
||||||
|
|
||||||
|
const bool keyOnly = authenticationType ==
|
||||||
|
SshConnectionParameters::AuthenticationTypeSpecificKey;
|
||||||
|
if (keyOnly) {
|
||||||
|
args << "-o" << "IdentitiesOnly=yes";
|
||||||
|
args << "-i" << privateKeyFile.path();
|
||||||
|
}
|
||||||
|
if (keyOnly || SshSettings::askpassFilePath().isEmpty())
|
||||||
|
args << "-o" << "BatchMode=yes";
|
||||||
|
|
||||||
|
bool useTimeout = timeout != 0;
|
||||||
|
if (useTimeout && HostOsInfo::isWindowsHost()
|
||||||
|
&& binary.toString().toLower().contains("/system32/")) {
|
||||||
|
useTimeout = false;
|
||||||
|
}
|
||||||
|
if (useTimeout)
|
||||||
|
args << "-o" << ("ConnectTimeout=" + QString::number(timeout));
|
||||||
|
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
static inline bool equals(const SshConnectionParameters &p1, const SshConnectionParameters &p2)
|
static inline bool equals(const SshConnectionParameters &p1, const SshConnectionParameters &p2)
|
||||||
{
|
{
|
||||||
return p1.url == p2.url
|
return p1.url == p2.url
|
||||||
@@ -113,38 +153,10 @@ struct SshConnection::SshConnectionPrivate
|
|||||||
|
|
||||||
QStringList connectionOptions(const FilePath &binary) const
|
QStringList connectionOptions(const FilePath &binary) const
|
||||||
{
|
{
|
||||||
QString hostKeyCheckingString;
|
QStringList options = connParams.connectionOptions(binary);
|
||||||
switch (connParams.hostKeyCheckingMode) {
|
|
||||||
case SshHostKeyCheckingNone:
|
|
||||||
case SshHostKeyCheckingAllowNoMatch:
|
|
||||||
// There is "accept-new" as well, but only since 7.6.
|
|
||||||
hostKeyCheckingString = "no";
|
|
||||||
break;
|
|
||||||
case SshHostKeyCheckingStrict:
|
|
||||||
hostKeyCheckingString = "yes";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
QStringList args{"-o", "StrictHostKeyChecking=" + hostKeyCheckingString,
|
|
||||||
"-o", "User=" + connParams.userName(),
|
|
||||||
"-o", "Port=" + QString::number(connParams.port())};
|
|
||||||
const bool keyOnly = connParams.authenticationType ==
|
|
||||||
SshConnectionParameters::AuthenticationTypeSpecificKey;
|
|
||||||
if (keyOnly) {
|
|
||||||
args << "-o" << "IdentitiesOnly=yes";
|
|
||||||
args << "-i" << connParams.privateKeyFile.path();
|
|
||||||
}
|
|
||||||
if (keyOnly || SshSettings::askpassFilePath().isEmpty())
|
|
||||||
args << "-o" << "BatchMode=yes";
|
|
||||||
if (sharingEnabled)
|
if (sharingEnabled)
|
||||||
args << "-o" << ("ControlPath=" + socketFilePath());
|
options << "-o" << ("ControlPath=" + socketFilePath());
|
||||||
bool useTimeout = connParams.timeout != 0;
|
return options;
|
||||||
if (useTimeout && HostOsInfo::isWindowsHost()
|
|
||||||
&& binary.toString().toLower().contains("/system32/")) {
|
|
||||||
useTimeout = false;
|
|
||||||
}
|
|
||||||
if (useTimeout)
|
|
||||||
args << "-o" << ("ConnectTimeout=" + QString::number(connParams.timeout));
|
|
||||||
return args;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList connectionArgs(const FilePath &binary) const
|
QStringList connectionArgs(const FilePath &binary) const
|
||||||
|
|||||||
@@ -65,6 +65,8 @@ public:
|
|||||||
void setPort(int port) { url.setPort(port); }
|
void setPort(int port) { url.setPort(port); }
|
||||||
void setUserName(const QString &name) { url.setUserName(name); }
|
void setUserName(const QString &name) { url.setUserName(name); }
|
||||||
|
|
||||||
|
QStringList connectionOptions(const Utils::FilePath &binary) const;
|
||||||
|
|
||||||
QUrl url;
|
QUrl url;
|
||||||
Utils::FilePath privateKeyFile;
|
Utils::FilePath privateKeyFile;
|
||||||
QString x11DisplayName;
|
QString x11DisplayName;
|
||||||
|
|||||||
@@ -25,21 +25,21 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "ssh_global.h"
|
||||||
#include "sshprocess.h"
|
#include "sshprocess.h"
|
||||||
|
|
||||||
namespace QSsh {
|
namespace Utils { class CommandLine; }
|
||||||
|
|
||||||
class SshConnection;
|
namespace QSsh {
|
||||||
|
|
||||||
class QSSH_EXPORT SshRemoteProcess : public SshProcess
|
class QSSH_EXPORT SshRemoteProcess : public SshProcess
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
friend class SshConnection;
|
public:
|
||||||
SshRemoteProcess(const QString &command, const QStringList &connectionArgs,
|
SshRemoteProcess(const QString &command, const QStringList &connectionArgs,
|
||||||
Utils::ProcessMode processMode = Utils::ProcessMode::Reader);
|
Utils::ProcessMode processMode = Utils::ProcessMode::Reader);
|
||||||
|
|
||||||
public:
|
|
||||||
void requestX11Forwarding(const QString &displayName);
|
void requestX11Forwarding(const QString &displayName);
|
||||||
void start();
|
void start();
|
||||||
|
|
||||||
@@ -49,6 +49,8 @@ signals:
|
|||||||
void done(const QString &error);
|
void done(const QString &error);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void doStart();
|
||||||
|
|
||||||
QString m_remoteCommand;
|
QString m_remoteCommand;
|
||||||
QStringList m_connectionArgs;
|
QStringList m_connectionArgs;
|
||||||
QString m_displayName;
|
QString m_displayName;
|
||||||
|
|||||||
@@ -1519,7 +1519,6 @@ static void filterEntriesHelper(const FilePath &base,
|
|||||||
// FIXME: Handle filters. For now bark on unsupported options.
|
// FIXME: Handle filters. For now bark on unsupported options.
|
||||||
QTC_CHECK(filters == QDir::NoFilter);
|
QTC_CHECK(filters == QDir::NoFilter);
|
||||||
|
|
||||||
FilePaths result;
|
|
||||||
for (const QString &entry : entries) {
|
for (const QString &entry : entries) {
|
||||||
if (!nameMatches(entry))
|
if (!nameMatches(entry))
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ add_qtc_plugin(RemoteLinux
|
|||||||
abstractremotelinuxdeploystep.cpp abstractremotelinuxdeploystep.h
|
abstractremotelinuxdeploystep.cpp abstractremotelinuxdeploystep.h
|
||||||
abstractuploadandinstallpackageservice.cpp abstractuploadandinstallpackageservice.h
|
abstractuploadandinstallpackageservice.cpp abstractuploadandinstallpackageservice.h
|
||||||
deploymenttimeinfo.cpp deploymenttimeinfo.h
|
deploymenttimeinfo.cpp deploymenttimeinfo.h
|
||||||
|
filesystemaccess_test.cpp filesystemaccess_test.h
|
||||||
genericdirectuploadservice.cpp genericdirectuploadservice.h
|
genericdirectuploadservice.cpp genericdirectuploadservice.h
|
||||||
genericdirectuploadstep.cpp genericdirectuploadstep.h
|
genericdirectuploadstep.cpp genericdirectuploadstep.h
|
||||||
genericlinuxdeviceconfigurationwidget.cpp genericlinuxdeviceconfigurationwidget.h genericlinuxdeviceconfigurationwidget.ui
|
genericlinuxdeviceconfigurationwidget.cpp genericlinuxdeviceconfigurationwidget.h genericlinuxdeviceconfigurationwidget.ui
|
||||||
|
|||||||
141
src/plugins/remotelinux/filesystemaccess_test.cpp
Normal file
141
src/plugins/remotelinux/filesystemaccess_test.cpp
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "filesystemaccess_test.h"
|
||||||
|
|
||||||
|
#include "linuxdevice.h"
|
||||||
|
#include "remotelinux_constants.h"
|
||||||
|
|
||||||
|
#include <utils/filepath.h>
|
||||||
|
#include <projectexplorer/devicesupport/devicemanager.h>
|
||||||
|
#include <ssh/sshconnection.h>
|
||||||
|
|
||||||
|
#include <QTest>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
using namespace ProjectExplorer;
|
||||||
|
using namespace Utils;
|
||||||
|
|
||||||
|
namespace RemoteLinux {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
static const char TEST_IP[] = "127.0.0.1";
|
||||||
|
|
||||||
|
TestLinuxDeviceFactory::TestLinuxDeviceFactory()
|
||||||
|
: IDeviceFactory("test")
|
||||||
|
{
|
||||||
|
setDisplayName("Generic Linux Device");
|
||||||
|
setIcon(QIcon());
|
||||||
|
setCanCreate(true);
|
||||||
|
setConstructionFunction(&LinuxDevice::create);
|
||||||
|
}
|
||||||
|
|
||||||
|
IDevice::Ptr TestLinuxDeviceFactory::create() const
|
||||||
|
{
|
||||||
|
LinuxDevice::Ptr newDev = LinuxDevice::create();
|
||||||
|
qDebug() << "device : " << newDev->type();
|
||||||
|
newDev->setType("test");
|
||||||
|
QSsh::SshConnectionParameters sshParams = newDev->sshParameters();
|
||||||
|
sshParams.setHost(TEST_IP);
|
||||||
|
sshParams.setPort(22);
|
||||||
|
newDev->setSshParameters(sshParams);
|
||||||
|
return newDev;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSystemAccessTest::initTestCase()
|
||||||
|
{
|
||||||
|
FilePath filePath = FilePath::fromString("ssh://"+ QString(TEST_IP) +"/tmp");
|
||||||
|
|
||||||
|
if (DeviceManager::deviceForPath(filePath) == nullptr) {
|
||||||
|
DeviceManager *const devMgr = DeviceManager::instance();
|
||||||
|
const IDevice::Ptr newDev = m_testLinuxDeviceFactory.create();
|
||||||
|
QVERIFY(!newDev.isNull());
|
||||||
|
devMgr->addDevice(newDev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSystemAccessTest::testDirStatuses()
|
||||||
|
{
|
||||||
|
FilePath filePath = FilePath::fromString("ssh://" + QString(TEST_IP) + "/tmp");
|
||||||
|
QVERIFY(filePath.exists());
|
||||||
|
QVERIFY(filePath.isDir());
|
||||||
|
QVERIFY(filePath.isWritableDir());
|
||||||
|
|
||||||
|
FilePath testFilePath = FilePath::fromString("ssh://" + QString(TEST_IP) + "/tmp/test");
|
||||||
|
FilePath dummyFilePath = FilePath::fromString("ssh://" + QString(TEST_IP) + "/dev/null");
|
||||||
|
dummyFilePath.copyFile(testFilePath);
|
||||||
|
QVERIFY(testFilePath.exists());
|
||||||
|
QVERIFY(testFilePath.isFile());
|
||||||
|
|
||||||
|
bool fileExists = false;
|
||||||
|
filePath.iterateDirectory(
|
||||||
|
[&fileExists](const FilePath &filePath) {
|
||||||
|
if (filePath.baseName() == "test"){
|
||||||
|
fileExists = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
{"test"},
|
||||||
|
QDir::Files);
|
||||||
|
|
||||||
|
QVERIFY(fileExists);
|
||||||
|
QVERIFY(testFilePath.removeFile());
|
||||||
|
QVERIFY(!testFilePath.exists());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSystemAccessTest::testFileActions()
|
||||||
|
{
|
||||||
|
FilePath testFilePath = FilePath::fromString("ssh://" + QString(TEST_IP) + "/tmp/test");
|
||||||
|
FilePath dummyFilePath = FilePath::fromString("ssh://" + QString(TEST_IP) + "/dev/null");
|
||||||
|
dummyFilePath.copyFile(testFilePath);
|
||||||
|
QVERIFY(testFilePath.exists());
|
||||||
|
QVERIFY(testFilePath.isFile());
|
||||||
|
|
||||||
|
testFilePath.setPermissions(QFile::ReadOther | QFile::WriteOther | QFile::ExeOther
|
||||||
|
| QFile::ReadGroup | QFile::WriteGroup | QFile::ExeGroup
|
||||||
|
| QFile::ReadUser | QFile::WriteUser | QFile::ExeUser);
|
||||||
|
QVERIFY(testFilePath.isWritableFile());
|
||||||
|
QVERIFY(testFilePath.isReadableFile());
|
||||||
|
QVERIFY(testFilePath.isExecutableFile());
|
||||||
|
|
||||||
|
QByteArray content("Test");
|
||||||
|
testFilePath.writeFileContents(content);
|
||||||
|
// ToDo: remove ".contains", make fileContents exact equal content
|
||||||
|
QVERIFY(testFilePath.fileContents().contains(content));
|
||||||
|
|
||||||
|
QVERIFY(testFilePath.renameFile(FilePath::fromString("ssh://" + QString(TEST_IP) + "/tmp/test1")));
|
||||||
|
// It is Ok that FilePath doesn't change itself after rename.
|
||||||
|
FilePath newTestFilePath = FilePath::fromString("ssh://" + QString(TEST_IP) + "/tmp/test1");
|
||||||
|
QVERIFY(newTestFilePath.exists());
|
||||||
|
QVERIFY(!testFilePath.removeFile());
|
||||||
|
QVERIFY(newTestFilePath.exists());
|
||||||
|
QVERIFY(newTestFilePath.removeFile());
|
||||||
|
QVERIFY(!newTestFilePath.exists());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // Internal
|
||||||
|
} // RemoteLinux
|
||||||
61
src/plugins/remotelinux/filesystemaccess_test.h
Normal file
61
src/plugins/remotelinux/filesystemaccess_test.h
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2021 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <projectexplorer/devicesupport/idevice.h>
|
||||||
|
#include <projectexplorer/devicesupport/idevicefactory.h>
|
||||||
|
|
||||||
|
#include <utils/fileutils.h>
|
||||||
|
|
||||||
|
namespace RemoteLinux {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
class TestLinuxDeviceFactory final : public ProjectExplorer::IDeviceFactory
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TestLinuxDeviceFactory();
|
||||||
|
|
||||||
|
Utils::FilePaths defaultKeys() const;
|
||||||
|
|
||||||
|
ProjectExplorer::IDevice::Ptr create() const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FileSystemAccessTest : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void initTestCase();
|
||||||
|
|
||||||
|
void testDirStatuses();
|
||||||
|
void testFileActions();
|
||||||
|
|
||||||
|
private:
|
||||||
|
TestLinuxDeviceFactory m_testLinuxDeviceFactory;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // Internal
|
||||||
|
} // RemoteLinux
|
||||||
@@ -40,7 +40,9 @@
|
|||||||
#include <projectexplorer/devicesupport/sshdeviceprocesslist.h>
|
#include <projectexplorer/devicesupport/sshdeviceprocesslist.h>
|
||||||
#include <projectexplorer/runcontrol.h>
|
#include <projectexplorer/runcontrol.h>
|
||||||
|
|
||||||
|
#include <ssh/sshconnectionmanager.h>
|
||||||
#include <ssh/sshremoteprocessrunner.h>
|
#include <ssh/sshremoteprocessrunner.h>
|
||||||
|
#include <ssh/sshsettings.h>
|
||||||
|
|
||||||
#include <utils/algorithm.h>
|
#include <utils/algorithm.h>
|
||||||
#include <utils/environment.h>
|
#include <utils/environment.h>
|
||||||
@@ -48,8 +50,16 @@
|
|||||||
#include <utils/port.h>
|
#include <utils/port.h>
|
||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
#include <utils/stringutils.h>
|
#include <utils/stringutils.h>
|
||||||
|
#include <utils/temporaryfile.h>
|
||||||
|
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QLoggingCategory>
|
||||||
|
#include <QMutex>
|
||||||
|
#include <QRegularExpression>
|
||||||
|
#include <QThread>
|
||||||
|
|
||||||
using namespace ProjectExplorer;
|
using namespace ProjectExplorer;
|
||||||
|
using namespace QSsh;
|
||||||
using namespace Utils;
|
using namespace Utils;
|
||||||
|
|
||||||
namespace RemoteLinux {
|
namespace RemoteLinux {
|
||||||
@@ -57,6 +67,12 @@ namespace RemoteLinux {
|
|||||||
const char Delimiter0[] = "x--";
|
const char Delimiter0[] = "x--";
|
||||||
const char Delimiter1[] = "---";
|
const char Delimiter1[] = "---";
|
||||||
|
|
||||||
|
static Q_LOGGING_CATEGORY(linuxDeviceLog, "qtc.remotelinux.device", QtWarningMsg);
|
||||||
|
#define LOG(x) qCDebug(linuxDeviceLog) << x << '\n'
|
||||||
|
//#define DEBUG(x) qDebug() << x;
|
||||||
|
//#define DEBUG(x) LOG(x)
|
||||||
|
#define DEBUG(x)
|
||||||
|
|
||||||
static QString visualizeNull(QString s)
|
static QString visualizeNull(QString s)
|
||||||
{
|
{
|
||||||
return s.replace(QLatin1Char('\0'), QLatin1String("<null>"));
|
return s.replace(QLatin1Char('\0'), QLatin1String("<null>"));
|
||||||
@@ -170,12 +186,104 @@ class LinuxPortsGatheringMethod : public PortsGatheringMethod
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
IDeviceWidget *LinuxDevice::createWidget()
|
// ShellThreadHandler
|
||||||
|
|
||||||
|
class ShellThreadHandler : public QObject
|
||||||
{
|
{
|
||||||
return new GenericLinuxDeviceConfigurationWidget(sharedFromThis());
|
public:
|
||||||
|
~ShellThreadHandler()
|
||||||
|
{
|
||||||
|
if (m_shell)
|
||||||
|
delete m_shell;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool start(const SshConnectionParameters ¶meters)
|
||||||
|
{
|
||||||
|
m_shell = new SshRemoteProcess("/bin/sh",
|
||||||
|
parameters.connectionOptions(SshSettings::sshFilePath()) << parameters.host(),
|
||||||
|
ProcessMode::Writer);
|
||||||
|
m_shell->start();
|
||||||
|
const bool ret = m_shell->waitForStarted();
|
||||||
|
if (!ret) {
|
||||||
|
delete m_shell;
|
||||||
|
m_shell = nullptr;
|
||||||
|
DEBUG("Failed to connect to " << parameters.host());
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool runInShell(const CommandLine &cmd, const QByteArray &data = {})
|
||||||
|
{
|
||||||
|
QTC_ASSERT(m_shell, return false);
|
||||||
|
const QByteArray prefix = !data.isEmpty() ? QByteArray("echo " + data + " | ")
|
||||||
|
: QByteArray("");
|
||||||
|
|
||||||
|
m_shell->readAllStandardOutput(); // clean possible left-overs
|
||||||
|
m_shell->write(prefix + cmd.toUserOutput().toUtf8() + "\necho $?\n");
|
||||||
|
DEBUG("RUN1 " << cmd.toUserOutput());
|
||||||
|
m_shell->waitForReadyRead();
|
||||||
|
const QByteArray output = m_shell->readAllStandardOutput();
|
||||||
|
DEBUG("GOT1 " << output);
|
||||||
|
bool ok = false;
|
||||||
|
const int result = output.toInt(&ok);
|
||||||
|
LOG("Run command in shell:" << cmd.toUserOutput() << "result: " << output << " ==>" << result);
|
||||||
|
return ok && result == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString outputForRunInShell(const CommandLine &cmd)
|
||||||
|
{
|
||||||
|
QTC_ASSERT(m_shell, return {});
|
||||||
|
|
||||||
|
static int val = 0;
|
||||||
|
const QByteArray delim = QString::number(++val, 16).toUtf8();
|
||||||
|
|
||||||
|
DEBUG("RUN2 " << cmd.toUserOutput());
|
||||||
|
m_shell->readAllStandardOutput(); // clean possible left-overs
|
||||||
|
const QByteArray marker = "___QTC___" + delim + "_OUTPUT_MARKER___";
|
||||||
|
DEBUG(" CMD: " << cmd.toUserOutput().toUtf8() + "\necho " + marker + "\n");
|
||||||
|
m_shell->write(cmd.toUserOutput().toUtf8() + "\necho " + marker + "\n");
|
||||||
|
QByteArray output;
|
||||||
|
while (!output.contains(marker)) {
|
||||||
|
DEBUG("OUTPUT" << output);
|
||||||
|
m_shell->waitForReadyRead();
|
||||||
|
output.append(m_shell->readAllStandardOutput());
|
||||||
|
}
|
||||||
|
DEBUG("GOT2 " << output);
|
||||||
|
LOG("Run command in shell:" << cmd.toUserOutput() << "output size:" << output.size());
|
||||||
|
const int pos = output.indexOf(marker);
|
||||||
|
if (pos >= 0)
|
||||||
|
output = output.left(pos);
|
||||||
|
DEBUG("CHOPPED2 " << output);
|
||||||
|
return QString::fromUtf8(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isRunning() const { return m_shell; }
|
||||||
|
private:
|
||||||
|
SshRemoteProcess *m_shell = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
// LinuxDevicePrivate
|
||||||
|
|
||||||
|
class LinuxDevicePrivate
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit LinuxDevicePrivate(LinuxDevice *parent);
|
||||||
|
~LinuxDevicePrivate();
|
||||||
|
|
||||||
|
bool setupShell();
|
||||||
|
bool runInShell(const CommandLine &cmd, const QByteArray &data = {});
|
||||||
|
QString outputForRunInShell(const CommandLine &cmd);
|
||||||
|
|
||||||
|
LinuxDevice *q = nullptr;
|
||||||
|
QThread m_shellThread;
|
||||||
|
ShellThreadHandler *m_handler = nullptr;
|
||||||
|
mutable QMutex m_shellMutex;
|
||||||
|
};
|
||||||
|
|
||||||
|
// LinuxDevice
|
||||||
|
|
||||||
LinuxDevice::LinuxDevice()
|
LinuxDevice::LinuxDevice()
|
||||||
|
: d(new LinuxDevicePrivate(this))
|
||||||
{
|
{
|
||||||
setDisplayType(tr("Generic Linux"));
|
setDisplayType(tr("Generic Linux"));
|
||||||
setDefaultDisplayName(tr("Generic Linux Device"));
|
setDefaultDisplayName(tr("Generic Linux Device"));
|
||||||
@@ -222,6 +330,16 @@ LinuxDevice::LinuxDevice()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LinuxDevice::~LinuxDevice()
|
||||||
|
{
|
||||||
|
delete d;
|
||||||
|
}
|
||||||
|
|
||||||
|
IDeviceWidget *LinuxDevice::createWidget()
|
||||||
|
{
|
||||||
|
return new GenericLinuxDeviceConfigurationWidget(sharedFromThis());
|
||||||
|
}
|
||||||
|
|
||||||
DeviceProcess *LinuxDevice::createProcess(QObject *parent) const
|
DeviceProcess *LinuxDevice::createProcess(QObject *parent) const
|
||||||
{
|
{
|
||||||
return new LinuxDeviceProcess(sharedFromThis(), parent);
|
return new LinuxDeviceProcess(sharedFromThis(), parent);
|
||||||
@@ -277,6 +395,305 @@ DeviceEnvironmentFetcher::Ptr LinuxDevice::environmentFetcher() const
|
|||||||
return DeviceEnvironmentFetcher::Ptr(new LinuxDeviceEnvironmentFetcher(sharedFromThis()));
|
return DeviceEnvironmentFetcher::Ptr(new LinuxDeviceEnvironmentFetcher(sharedFromThis()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString LinuxDevice::userAtHost() const
|
||||||
|
{
|
||||||
|
if (sshParameters().userName().isEmpty())
|
||||||
|
return sshParameters().host();
|
||||||
|
return sshParameters().userName() + '@' + sshParameters().host();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LinuxDevice::handlesFile(const FilePath &filePath) const
|
||||||
|
{
|
||||||
|
DEBUG("handlesFile " << filePath.scheme() << filePath.host() << userAtHost());
|
||||||
|
return filePath.scheme() == "ssh" && filePath.host() == userAtHost();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LinuxDevice::runProcess(QtcProcess &process) const
|
||||||
|
{
|
||||||
|
QTC_CHECK(false); // FIXME: Implement
|
||||||
|
}
|
||||||
|
|
||||||
|
LinuxDevicePrivate::LinuxDevicePrivate(LinuxDevice *parent)
|
||||||
|
: q(parent)
|
||||||
|
{
|
||||||
|
m_handler = new ShellThreadHandler();
|
||||||
|
m_handler->moveToThread(&m_shellThread);
|
||||||
|
QObject::connect(&m_shellThread, &QThread::finished, m_handler, &QObject::deleteLater);
|
||||||
|
m_shellThread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
LinuxDevicePrivate::~LinuxDevicePrivate()
|
||||||
|
{
|
||||||
|
m_shellThread.quit();
|
||||||
|
m_shellThread.wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LinuxDevicePrivate::setupShell()
|
||||||
|
{
|
||||||
|
bool ok = false;
|
||||||
|
QMetaObject::invokeMethod(m_handler, [this, parameters = q->sshParameters()] {
|
||||||
|
return m_handler->start(parameters);
|
||||||
|
}, Qt::BlockingQueuedConnection, &ok);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LinuxDevicePrivate::runInShell(const CommandLine &cmd, const QByteArray &data)
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_shellMutex);
|
||||||
|
DEBUG(cmd.toUserOutput());
|
||||||
|
if (!m_handler->isRunning()) {
|
||||||
|
const bool ok = setupShell();
|
||||||
|
QTC_ASSERT(ok, return false);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ret = false;
|
||||||
|
QMetaObject::invokeMethod(m_handler, [this, &cmd, &data] {
|
||||||
|
return m_handler->runInShell(cmd, data);
|
||||||
|
}, Qt::BlockingQueuedConnection, &ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString LinuxDevicePrivate::outputForRunInShell(const CommandLine &cmd)
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_shellMutex);
|
||||||
|
DEBUG(cmd.toUserOutput());
|
||||||
|
if (!m_handler->isRunning()) {
|
||||||
|
const bool ok = setupShell();
|
||||||
|
QTC_ASSERT(ok, return {});
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ret;
|
||||||
|
QMetaObject::invokeMethod(m_handler, [this, &cmd] {
|
||||||
|
return m_handler->outputForRunInShell(cmd);
|
||||||
|
}, Qt::BlockingQueuedConnection, &ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LinuxDevice::isExecutableFile(const FilePath &filePath) const
|
||||||
|
{
|
||||||
|
QTC_ASSERT(handlesFile(filePath), return false);
|
||||||
|
const QString path = filePath.path();
|
||||||
|
return d->runInShell({"test", {"-x", path}});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LinuxDevice::isReadableFile(const FilePath &filePath) const
|
||||||
|
{
|
||||||
|
QTC_ASSERT(handlesFile(filePath), return false);
|
||||||
|
const QString path = filePath.path();
|
||||||
|
return d->runInShell({"test", {"-r", path, "-a", "-f", path}});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LinuxDevice::isWritableFile(const FilePath &filePath) const
|
||||||
|
{
|
||||||
|
QTC_ASSERT(handlesFile(filePath), return false);
|
||||||
|
const QString path = filePath.path();
|
||||||
|
return d->runInShell({"test", {"-w", path, "-a", "-f", path}});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LinuxDevice::isReadableDirectory(const FilePath &filePath) const
|
||||||
|
{
|
||||||
|
QTC_ASSERT(handlesFile(filePath), return false);
|
||||||
|
const QString path = filePath.path();
|
||||||
|
return d->runInShell({"test", {"-r", path, "-a", "-d", path}});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LinuxDevice::isWritableDirectory(const FilePath &filePath) const
|
||||||
|
{
|
||||||
|
QTC_ASSERT(handlesFile(filePath), return false);
|
||||||
|
const QString path = filePath.path();
|
||||||
|
return d->runInShell({"test", {"-w", path, "-a", "-d", path}});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LinuxDevice::isFile(const FilePath &filePath) const
|
||||||
|
{
|
||||||
|
QTC_ASSERT(handlesFile(filePath), return false);
|
||||||
|
const QString path = filePath.path();
|
||||||
|
return d->runInShell({"test", {"-f", path}});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LinuxDevice::isDirectory(const FilePath &filePath) const
|
||||||
|
{
|
||||||
|
QTC_ASSERT(handlesFile(filePath), return false);
|
||||||
|
const QString path = filePath.path();
|
||||||
|
return d->runInShell({"test", {"-d", path}});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LinuxDevice::createDirectory(const FilePath &filePath) const
|
||||||
|
{
|
||||||
|
QTC_ASSERT(handlesFile(filePath), return false);
|
||||||
|
const QString path = filePath.path();
|
||||||
|
return d->runInShell({"mkdir", {"-p", path}});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LinuxDevice::exists(const FilePath &filePath) const
|
||||||
|
{
|
||||||
|
DEBUG("filepath " << filePath.path());
|
||||||
|
QTC_ASSERT(handlesFile(filePath), return false);
|
||||||
|
const QString path = filePath.path();
|
||||||
|
return d->runInShell({"test", {"-e", path}});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LinuxDevice::ensureExistingFile(const FilePath &filePath) const
|
||||||
|
{
|
||||||
|
QTC_ASSERT(handlesFile(filePath), return false);
|
||||||
|
const QString path = filePath.path();
|
||||||
|
return d->runInShell({"touch", {path}});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LinuxDevice::removeFile(const FilePath &filePath) const
|
||||||
|
{
|
||||||
|
QTC_ASSERT(handlesFile(filePath), return false);
|
||||||
|
return d->runInShell({"rm", {filePath.path()}});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LinuxDevice::removeRecursively(const FilePath &filePath) const
|
||||||
|
{
|
||||||
|
QTC_ASSERT(handlesFile(filePath), return false);
|
||||||
|
QTC_ASSERT(filePath.path().startsWith('/'), return false);
|
||||||
|
|
||||||
|
const QString path = filePath.cleanPath().path();
|
||||||
|
// We are expecting this only to be called in a context of build directories or similar.
|
||||||
|
// Chicken out in some cases that _might_ be user code errors.
|
||||||
|
QTC_ASSERT(path.startsWith('/'), return false);
|
||||||
|
const int levelsNeeded = path.startsWith("/home/") ? 4 : 3;
|
||||||
|
QTC_ASSERT(path.count('/') >= levelsNeeded, return false);
|
||||||
|
|
||||||
|
return d->runInShell({"rm", {"-rf", "--", path}});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LinuxDevice::copyFile(const FilePath &filePath, const FilePath &target) const
|
||||||
|
{
|
||||||
|
QTC_ASSERT(handlesFile(filePath), return false);
|
||||||
|
QTC_ASSERT(handlesFile(target), return false);
|
||||||
|
return d->runInShell({"cp", {filePath.path(), target.path()}});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LinuxDevice::renameFile(const FilePath &filePath, const FilePath &target) const
|
||||||
|
{
|
||||||
|
QTC_ASSERT(handlesFile(filePath), return false);
|
||||||
|
QTC_ASSERT(handlesFile(target), return false);
|
||||||
|
return d->runInShell({"mv", {filePath.path(), target.path()}});
|
||||||
|
}
|
||||||
|
|
||||||
|
QDateTime LinuxDevice::lastModified(const FilePath &filePath) const
|
||||||
|
{
|
||||||
|
QTC_ASSERT(handlesFile(filePath), return {});
|
||||||
|
const QString output = d->outputForRunInShell({"stat", {"-c", "%Y", filePath.path()}});
|
||||||
|
const qint64 secs = output.toLongLong();
|
||||||
|
const QDateTime dt = QDateTime::fromSecsSinceEpoch(secs, Qt::UTC);
|
||||||
|
return dt;
|
||||||
|
}
|
||||||
|
|
||||||
|
FilePath LinuxDevice::symLinkTarget(const FilePath &filePath) const
|
||||||
|
{
|
||||||
|
QTC_ASSERT(handlesFile(filePath), return {});
|
||||||
|
const QString output = d->outputForRunInShell({"readlink", {"-n", "-e", filePath.path()}});
|
||||||
|
return output.isEmpty() ? FilePath() : filePath.withNewPath(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
qint64 LinuxDevice::fileSize(const FilePath &filePath) const
|
||||||
|
{
|
||||||
|
QTC_ASSERT(handlesFile(filePath), return -1);
|
||||||
|
const QString output = d->outputForRunInShell({"stat", {"-c", "%s", filePath.path()}});
|
||||||
|
return output.toLongLong();
|
||||||
|
}
|
||||||
|
|
||||||
|
QFileDevice::Permissions LinuxDevice::permissions(const FilePath &filePath) const
|
||||||
|
{
|
||||||
|
QTC_ASSERT(handlesFile(filePath), return {});
|
||||||
|
const QString output = d->outputForRunInShell({"stat", {"-c", "%a", filePath.path()}});
|
||||||
|
const uint bits = output.toUInt(nullptr, 8);
|
||||||
|
QFileDevice::Permissions perm = {};
|
||||||
|
#define BIT(n, p) if (bits & (1<<n)) perm |= QFileDevice::p
|
||||||
|
BIT(0, ExeOther);
|
||||||
|
BIT(1, WriteOther);
|
||||||
|
BIT(2, ReadOther);
|
||||||
|
BIT(3, ExeGroup);
|
||||||
|
BIT(4, WriteGroup);
|
||||||
|
BIT(5, ReadGroup);
|
||||||
|
BIT(6, ExeUser);
|
||||||
|
BIT(7, WriteUser);
|
||||||
|
BIT(8, ReadUser);
|
||||||
|
#undef BIT
|
||||||
|
return perm;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LinuxDevice::setPermissions(const Utils::FilePath &filePath, QFileDevice::Permissions permissions) const
|
||||||
|
{
|
||||||
|
QTC_ASSERT(handlesFile(filePath), return false);
|
||||||
|
const int flags = int(permissions);
|
||||||
|
return d->runInShell({"chmod", {QString::number(flags, 16), filePath.path()}});
|
||||||
|
}
|
||||||
|
|
||||||
|
static void filterEntriesHelper(const FilePath &base,
|
||||||
|
const std::function<bool(const FilePath &)> &callBack,
|
||||||
|
const QStringList &entries,
|
||||||
|
const QStringList &nameFilters,
|
||||||
|
QDir::Filters filters)
|
||||||
|
{
|
||||||
|
const QList<QRegularExpression> nameRegexps = transform(nameFilters, [](const QString &filter) {
|
||||||
|
QRegularExpression re;
|
||||||
|
re.setPattern(QRegularExpression::wildcardToRegularExpression(filter));
|
||||||
|
QTC_CHECK(re.isValid());
|
||||||
|
return re;
|
||||||
|
});
|
||||||
|
|
||||||
|
const auto nameMatches = [&nameRegexps](const QString &fileName) {
|
||||||
|
for (const QRegularExpression &re : nameRegexps) {
|
||||||
|
const QRegularExpressionMatch match = re.match(fileName);
|
||||||
|
if (match.hasMatch())
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return nameRegexps.isEmpty();
|
||||||
|
};
|
||||||
|
|
||||||
|
// FIXME: Handle filters. For now bark on unsupported options.
|
||||||
|
QTC_CHECK(filters == QDir::NoFilter);
|
||||||
|
|
||||||
|
for (const QString &entry : entries) {
|
||||||
|
if (!nameMatches(entry))
|
||||||
|
continue;
|
||||||
|
if (!callBack(base.pathAppended(entry)))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LinuxDevice::iterateDirectory(const FilePath &filePath,
|
||||||
|
const std::function<bool(const FilePath &)> &callBack,
|
||||||
|
const QStringList &nameFilters,
|
||||||
|
QDir::Filters filters) const
|
||||||
|
{
|
||||||
|
QTC_ASSERT(handlesFile(filePath), return);
|
||||||
|
// if we do not have find - use ls as fallback
|
||||||
|
const QString output = d->outputForRunInShell({"ls", {"-1", "-b", "--", filePath.path()}});
|
||||||
|
const QStringList entries = output.split('\n', Qt::SkipEmptyParts);
|
||||||
|
filterEntriesHelper(filePath, callBack, entries, nameFilters, filters);
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray LinuxDevice::fileContents(const FilePath &filePath, qint64 limit, qint64 offset) const
|
||||||
|
{
|
||||||
|
QTC_ASSERT(handlesFile(filePath), return {});
|
||||||
|
QString args = "if=" + filePath.path() + " status=none";
|
||||||
|
if (limit > 0 || offset > 0) {
|
||||||
|
const qint64 gcd = std::gcd(limit, offset);
|
||||||
|
args += QString(" bs=%1 count=%2 seek=%3").arg(gcd).arg(limit / gcd).arg(offset / gcd);
|
||||||
|
}
|
||||||
|
CommandLine cmd(FilePath::fromString("dd"), args, CommandLine::Raw);
|
||||||
|
|
||||||
|
const QString output = d->outputForRunInShell(cmd);
|
||||||
|
DEBUG(output << output.toLatin1() << QByteArray::fromHex(output.toLatin1()));
|
||||||
|
return output.toLatin1();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LinuxDevice::writeFileContents(const FilePath &filePath, const QByteArray &data) const
|
||||||
|
{
|
||||||
|
QTC_ASSERT(handlesFile(filePath), return {});
|
||||||
|
|
||||||
|
// This following would be the generic Unix solution.
|
||||||
|
// But it doesn't pass input. FIXME: Why?
|
||||||
|
return d->runInShell({"dd", {"of=" + filePath.path()}}, data);
|
||||||
|
}
|
||||||
|
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
// Factory
|
// Factory
|
||||||
|
|||||||
@@ -40,6 +40,8 @@ public:
|
|||||||
using Ptr = QSharedPointer<LinuxDevice>;
|
using Ptr = QSharedPointer<LinuxDevice>;
|
||||||
using ConstPtr = QSharedPointer<const LinuxDevice>;
|
using ConstPtr = QSharedPointer<const LinuxDevice>;
|
||||||
|
|
||||||
|
~LinuxDevice();
|
||||||
|
|
||||||
static Ptr create() { return Ptr(new LinuxDevice); }
|
static Ptr create() { return Ptr(new LinuxDevice); }
|
||||||
|
|
||||||
ProjectExplorer::IDeviceWidget *createWidget() override;
|
ProjectExplorer::IDeviceWidget *createWidget() override;
|
||||||
@@ -55,8 +57,39 @@ public:
|
|||||||
ProjectExplorer::DeviceProcessSignalOperation::Ptr signalOperation() const override;
|
ProjectExplorer::DeviceProcessSignalOperation::Ptr signalOperation() const override;
|
||||||
ProjectExplorer::DeviceEnvironmentFetcher::Ptr environmentFetcher() const override;
|
ProjectExplorer::DeviceEnvironmentFetcher::Ptr environmentFetcher() const override;
|
||||||
|
|
||||||
|
QString userAtHost() const;
|
||||||
|
bool handlesFile(const Utils::FilePath &filePath) const override;
|
||||||
|
bool isExecutableFile(const Utils::FilePath &filePath) const override;
|
||||||
|
bool isReadableFile(const Utils::FilePath &filePath) const override;
|
||||||
|
bool isWritableFile(const Utils::FilePath &filePath) const override;
|
||||||
|
bool isReadableDirectory(const Utils::FilePath &filePath) const override;
|
||||||
|
bool isWritableDirectory(const Utils::FilePath &filePath) const override;
|
||||||
|
bool isFile(const Utils::FilePath &filePath) const override;
|
||||||
|
bool isDirectory(const Utils::FilePath &filePath) const override;
|
||||||
|
bool createDirectory(const Utils::FilePath &filePath) const override;
|
||||||
|
bool exists(const Utils::FilePath &filePath) const override;
|
||||||
|
bool ensureExistingFile(const Utils::FilePath &filePath) const override;
|
||||||
|
bool removeFile(const Utils::FilePath &filePath) const override;
|
||||||
|
bool removeRecursively(const Utils::FilePath &filePath) const override;
|
||||||
|
bool copyFile(const Utils::FilePath &filePath, const Utils::FilePath &target) const override;
|
||||||
|
bool renameFile(const Utils::FilePath &filePath, const Utils::FilePath &target) const override;
|
||||||
|
Utils::FilePath symLinkTarget(const Utils::FilePath &filePath) const override;
|
||||||
|
void iterateDirectory(const Utils::FilePath &filePath,
|
||||||
|
const std::function<bool(const Utils::FilePath &)> &callBack,
|
||||||
|
const QStringList &nameFilters,
|
||||||
|
QDir::Filters filters) const override;
|
||||||
|
QByteArray fileContents(const Utils::FilePath &filePath, qint64 limit, qint64 offset) const override;
|
||||||
|
bool writeFileContents(const Utils::FilePath &filePath, const QByteArray &data) const override;
|
||||||
|
QDateTime lastModified(const Utils::FilePath &filePath) const override;
|
||||||
|
void runProcess(Utils::QtcProcess &process) const override;
|
||||||
|
qint64 fileSize(const Utils::FilePath &filePath) const override;
|
||||||
|
QFileDevice::Permissions permissions(const Utils::FilePath &filePath) const override;
|
||||||
|
bool setPermissions(const Utils::FilePath &filePath, QFileDevice::Permissions permissions) const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
LinuxDevice();
|
LinuxDevice();
|
||||||
|
|
||||||
|
class LinuxDevicePrivate *d;
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|||||||
@@ -43,6 +43,10 @@
|
|||||||
#include "tarpackagecreationstep.h"
|
#include "tarpackagecreationstep.h"
|
||||||
#include "uploadandinstalltarpackagestep.h"
|
#include "uploadandinstalltarpackagestep.h"
|
||||||
|
|
||||||
|
#ifdef WITH_TESTS
|
||||||
|
#include "filesystemaccess_test.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <projectexplorer/kitinformation.h>
|
#include <projectexplorer/kitinformation.h>
|
||||||
#include <projectexplorer/target.h>
|
#include <projectexplorer/target.h>
|
||||||
|
|
||||||
@@ -121,6 +125,15 @@ RemoteLinuxPlugin::~RemoteLinuxPlugin()
|
|||||||
delete dd;
|
delete dd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QVector<QObject *> RemoteLinuxPlugin::createTestObjects() const
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
#ifdef WITH_TESTS
|
||||||
|
new FileSystemAccessTest,
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
bool RemoteLinuxPlugin::initialize(const QStringList &arguments, QString *errorMessage)
|
bool RemoteLinuxPlugin::initialize(const QStringList &arguments, QString *errorMessage)
|
||||||
{
|
{
|
||||||
Q_UNUSED(arguments)
|
Q_UNUSED(arguments)
|
||||||
|
|||||||
@@ -39,6 +39,8 @@ public:
|
|||||||
RemoteLinuxPlugin();
|
RemoteLinuxPlugin();
|
||||||
~RemoteLinuxPlugin() final;
|
~RemoteLinuxPlugin() final;
|
||||||
|
|
||||||
|
QVector<QObject *> createTestObjects() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool initialize(const QStringList &arguments, QString *errorMessage) final;
|
bool initialize(const QStringList &arguments, QString *errorMessage) final;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user