Move FileTransfer into ProjectExplorer plugin

Make it ready for providing implementations for other devices.

Change-Id: I14eaf167a7b2c1189f4d23f2e9f556204295b9b3
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
This commit is contained in:
Jarek Kobus
2022-05-20 17:53:20 +02:00
parent 48e2f79e8b
commit 7d848364ff
17 changed files with 425 additions and 322 deletions

View File

@@ -60,6 +60,8 @@ add_qtc_plugin(ProjectExplorer
devicesupport/devicesettingswidget.cpp devicesupport/devicesettingswidget.h devicesupport/devicesettingswidget.ui
devicesupport/devicetestdialog.cpp devicesupport/devicetestdialog.h devicesupport/devicetestdialog.ui
devicesupport/deviceusedportsgatherer.cpp devicesupport/deviceusedportsgatherer.h
devicesupport/filetransfer.cpp devicesupport/filetransfer.h
devicesupport/filetransferinterface.h
devicesupport/idevice.cpp devicesupport/idevice.h
devicesupport/idevicefactory.cpp devicesupport/idevicefactory.h
devicesupport/idevicefwd.h

View File

@@ -0,0 +1,227 @@
/****************************************************************************
**
** Copyright (C) 2022 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 "filetransfer.h"
#include "devicemanager.h"
#include "idevice.h"
#include <utils/processinterface.h>
#include <utils/qtcassert.h>
#include <QProcess>
using namespace Utils;
namespace ProjectExplorer {
FileTransferDirection FileToTransfer::direction() const
{
if (m_source.needsDevice() == m_target.needsDevice())
return FileTransferDirection::Invalid;
return m_source.needsDevice() ? FileTransferDirection::Download : FileTransferDirection::Upload;
}
QString FileTransferSetupData::defaultRsyncFlags()
{
return "-av";
}
static FileTransferDirection transferDirection(const FilesToTransfer &files)
{
if (files.isEmpty())
return FileTransferDirection::Invalid;
const FileTransferDirection direction = files.first().direction();
for (const FileToTransfer &file : files) {
if (file.direction() != direction)
return FileTransferDirection::Invalid;
}
return direction;
}
static const FilePath &remoteFile(FileTransferDirection direction, const FileToTransfer &file)
{
return direction == FileTransferDirection::Upload ? file.m_target : file.m_source;
}
static bool isSameDevice(const FilePath &first, const FilePath &second)
{
return (first.scheme() == second.scheme()) && (first.host() == second.host());
}
static IDeviceConstPtr matchedDevice(FileTransferDirection direction, const FilesToTransfer &files)
{
if (files.isEmpty())
return {};
const FilePath &filePath = remoteFile(direction, files.first());
for (const FileToTransfer &file : files) {
if (!isSameDevice(filePath, remoteFile(direction, file)))
return {};
}
return DeviceManager::deviceForPath(filePath);
}
void FileTransferInterface::startFailed(const QString &errorString)
{
emit done({0, QProcess::NormalExit, QProcess::FailedToStart, errorString});
}
class FileTransferPrivate : public QObject
{
Q_OBJECT
public:
void test(const ProjectExplorer::IDeviceConstPtr &onDevice);
void start();
void stop();
FileTransferSetupData m_setup;
signals:
void progress(const QString &progressMessage);
void done(const ProcessResultData &resultData);
private:
void startFailed(const QString &errorString);
void run(const FileTransferSetupData &setup, const IDeviceConstPtr &device);
std::unique_ptr<FileTransferInterface> m_transfer;
};
void FileTransferPrivate::test(const IDeviceConstPtr &onDevice)
{
if (!onDevice)
return startFailed(tr("No device set for test transfer."));
run({{}, m_setup.m_method, m_setup.m_rsyncFlags}, onDevice);
}
void FileTransferPrivate::start()
{
if (m_setup.m_files.isEmpty())
return startFailed(tr("No files to transfer."));
const FileTransferDirection direction = transferDirection(m_setup.m_files);
if (direction == FileTransferDirection::Invalid)
return startFailed(tr("Mixing different types of transfer in one go."));
const IDeviceConstPtr device = matchedDevice(direction, m_setup.m_files);
if (!device)
return startFailed(tr("Trying to transfer into / from not matching device."));
run(m_setup, device);
}
void FileTransferPrivate::stop()
{
if (!m_transfer)
return;
m_transfer->disconnect();
m_transfer.release()->deleteLater();
}
void FileTransferPrivate::run(const FileTransferSetupData &setup, const IDeviceConstPtr &device)
{
stop();
m_transfer.reset(device->createFileTransferInterface(setup));
QTC_ASSERT(m_transfer, startFailed(tr("Missing transfer implementation.")); return);
m_transfer->setParent(this);
connect(m_transfer.get(), &FileTransferInterface::progress,
this, &FileTransferPrivate::progress);
connect(m_transfer.get(), &FileTransferInterface::done,
this, &FileTransferPrivate::done);
m_transfer->start();
}
void FileTransferPrivate::startFailed(const QString &errorString)
{
emit done({0, QProcess::NormalExit, QProcess::FailedToStart, errorString});
}
FileTransfer::FileTransfer()
: d(new FileTransferPrivate)
{
d->setParent(this);
connect(d, &FileTransferPrivate::progress, this, &FileTransfer::progress);
connect(d, &FileTransferPrivate::done, this, &FileTransfer::done);
}
FileTransfer::~FileTransfer()
{
stop();
delete d;
}
void FileTransfer::setFilesToTransfer(const FilesToTransfer &files)
{
d->m_setup.m_files = files;
}
void FileTransfer::setTransferMethod(FileTransferMethod method)
{
d->m_setup.m_method = method;
}
void FileTransfer::setRsyncFlags(const QString &flags)
{
d->m_setup.m_rsyncFlags = flags;
}
void FileTransfer::test(const ProjectExplorer::IDeviceConstPtr &onDevice)
{
d->test(onDevice);
}
FileTransferMethod FileTransfer::transferMethod() const
{
return d->m_setup.m_method;
}
void FileTransfer::start()
{
d->start();
}
void FileTransfer::stop()
{
d->stop();
}
QString FileTransfer::transferMethodName(FileTransferMethod method)
{
switch (method) {
case FileTransferMethod::Sftp: return FileTransfer::tr("sftp");
case FileTransferMethod::Rsync: return FileTransfer::tr("rsync");
}
QTC_CHECK(false);
return {};
}
} // namespace ProjectExplorer
#include "filetransfer.moc"

View File

@@ -25,33 +25,17 @@
#pragma once
#include "remotelinux_export.h"
#include <projectexplorer/devicesupport/idevicefwd.h>
#include <utils/filepath.h>
#include "../projectexplorer_export.h"
#include "filetransferinterface.h"
#include "idevicefwd.h"
namespace Utils { class ProcessResultData; }
namespace RemoteLinux {
class REMOTELINUX_EXPORT FileToTransfer
{
public:
Utils::FilePath m_source;
Utils::FilePath m_target;
};
using FilesToTransfer = QList<FileToTransfer>;
enum class FileTransferMethod {
Sftp,
Rsync,
Default = Sftp
};
namespace ProjectExplorer {
class FileTransferPrivate;
class REMOTELINUX_EXPORT FileTransfer : public QObject
class PROJECTEXPLORER_EXPORT FileTransfer : public QObject
{
Q_OBJECT
@@ -59,19 +43,17 @@ public:
FileTransfer();
~FileTransfer();
void setDevice(const ProjectExplorer::IDeviceConstPtr &device);
void setTransferMethod(FileTransferMethod method);
void setFilesToTransfer(const FilesToTransfer &files);
void setTransferMethod(FileTransferMethod method);
void setRsyncFlags(const QString &flags);
FileTransferMethod transferMethod() const;
void test();
void test(const ProjectExplorer::IDeviceConstPtr &onDevice);
void start();
void stop();
static QString transferMethodName(FileTransferMethod method);
static QString defaultRsyncFlags();
signals:
void progress(const QString &progressMessage);
@@ -81,6 +63,4 @@ private:
FileTransferPrivate *d;
};
} // namespace RemoteLinux
Q_DECLARE_METATYPE(RemoteLinux::FileTransferMethod)
} // namespace ProjectExplorer

View File

@@ -0,0 +1,93 @@
/****************************************************************************
**
** Copyright (C) 2022 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_export.h"
#include <utils/filepath.h>
namespace Utils { class ProcessResultData; }
namespace ProjectExplorer {
enum class FileTransferDirection {
Invalid,
Upload,
Download
};
enum class FileTransferMethod {
Sftp,
Rsync,
Default = Sftp
};
class PROJECTEXPLORER_EXPORT FileToTransfer
{
public:
Utils::FilePath m_source;
Utils::FilePath m_target;
FileTransferDirection direction() const;
};
using FilesToTransfer = QList<FileToTransfer>;
class PROJECTEXPLORER_EXPORT FileTransferSetupData
{
public:
FilesToTransfer m_files; // When empty, do test instead of a real transfer
FileTransferMethod m_method = FileTransferMethod::Default;
QString m_rsyncFlags = defaultRsyncFlags();
static QString defaultRsyncFlags();
};
class PROJECTEXPLORER_EXPORT FileTransferInterface : public QObject
{
Q_OBJECT
signals:
void progress(const QString &progressMessage);
void done(const Utils::ProcessResultData &resultData);
protected:
FileTransferInterface(const FileTransferSetupData &setupData)
: m_setup(setupData) {}
void startFailed(const QString &errorString);
const FileTransferSetupData m_setup;
private:
FileTransferInterface() = delete;
virtual void start() = 0;
friend class FileTransferPrivate;
};
} // namespace ProjectExplorer

View File

@@ -434,6 +434,14 @@ ProcessInterface *IDevice::createProcessInterface() const
return nullptr;
}
FileTransferInterface *IDevice::createFileTransferInterface(
const FileTransferSetupData &setup) const
{
Q_UNUSED(setup)
QTC_CHECK(false);
return nullptr;
}
Environment IDevice::systemEnvironment() const
{
QTC_CHECK(false);

View File

@@ -60,6 +60,8 @@ class QtcProcess;
namespace ProjectExplorer {
class DeviceProcessList;
class FileTransferInterface;
class FileTransferSetupData;
class Kit;
class SshParameters;
class Task;
@@ -265,6 +267,8 @@ public:
virtual QFile::Permissions permissions(const Utils::FilePath &filePath) const;
virtual bool setPermissions(const Utils::FilePath &filePath, QFile::Permissions) const;
virtual Utils::ProcessInterface *createProcessInterface() const;
virtual FileTransferInterface *createFileTransferInterface(
const FileTransferSetupData &setup) const;
virtual Utils::Environment systemEnvironment() const;
virtual qint64 fileSize(const Utils::FilePath &filePath) const;
virtual qint64 bytesAvailable(const Utils::FilePath &filePath) const;

View File

@@ -219,6 +219,8 @@ Project {
"devicesettingswidget.cpp", "devicesettingswidget.h", "devicesettingswidget.ui",
"devicetestdialog.cpp", "devicetestdialog.h", "devicetestdialog.ui",
"deviceusedportsgatherer.cpp", "deviceusedportsgatherer.h",
"filetransfer.cpp", "filetransfer.h",
"filetransferinterface.h",
"idevice.cpp", "idevice.h",
"idevicefactory.cpp", "idevicefactory.h",
"idevicefwd.h",

View File

@@ -6,7 +6,6 @@ add_qtc_plugin(RemoteLinux
abstractremotelinuxdeployservice.cpp abstractremotelinuxdeployservice.h
abstractremotelinuxdeploystep.cpp abstractremotelinuxdeploystep.h
deploymenttimeinfo.cpp deploymenttimeinfo.h
filetransfer.h
genericdirectuploadservice.cpp genericdirectuploadservice.h
genericdirectuploadstep.cpp genericdirectuploadstep.h
genericlinuxdeviceconfigurationwidget.cpp genericlinuxdeviceconfigurationwidget.h genericlinuxdeviceconfigurationwidget.ui

View File

@@ -25,10 +25,10 @@
#include "filesystemaccess_test.h"
#include "filetransfer.h"
#include "linuxdevice.h"
#include <projectexplorer/devicesupport/devicemanager.h>
#include <projectexplorer/devicesupport/filetransfer.h>
#include <projectexplorer/devicesupport/sshparameters.h>
#include <utils/filepath.h>
#include <utils/processinterface.h>
@@ -39,6 +39,8 @@
#include <QTest>
#include <QTimer>
Q_DECLARE_METATYPE(ProjectExplorer::FileTransferMethod)
using namespace ProjectExplorer;
using namespace Utils;
@@ -230,7 +232,6 @@ void FileSystemAccessTest::testFileTransfer()
FileTransfer fileTransfer;
fileTransfer.setTransferMethod(fileTransferMethod);
fileTransfer.setDevice(m_device);
// Create and upload 1000 small files and one big file
QTemporaryDir dirForFilesToUpload;

View File

@@ -25,9 +25,8 @@
#include "genericdirectuploadservice.h"
#include "filetransfer.h"
#include <projectexplorer/deployablefile.h>
#include <projectexplorer/devicesupport/filetransfer.h>
#include <projectexplorer/devicesupport/idevice.h>
#include <utils/hostosinfo.h>
#include <utils/processinterface.h>
@@ -310,7 +309,6 @@ void GenericDirectUploadService::uploadFiles()
deviceConfiguration()->filePath(file.remoteFilePath())});
}
d->uploader.setDevice(deviceConfiguration());
d->uploader.setFilesToTransfer(files);
d->uploader.start();
}

View File

@@ -25,7 +25,6 @@
#include "linuxdevice.h"
#include "filetransfer.h"
#include "genericlinuxdeviceconfigurationwidget.h"
#include "genericlinuxdeviceconfigurationwizard.h"
#include "linuxdevicetester.h"
@@ -39,6 +38,8 @@
#include <coreplugin/icore.h>
#include <coreplugin/messagemanager.h>
#include <projectexplorer/devicesupport/filetransfer.h>
#include <projectexplorer/devicesupport/filetransferinterface.h>
#include <projectexplorer/devicesupport/sshdeviceprocesslist.h>
#include <projectexplorer/devicesupport/sshparameters.h>
#include <projectexplorer/devicesupport/sshsettings.h>
@@ -1417,47 +1418,6 @@ bool LinuxDevice::writeFileContents(const FilePath &filePath, const QByteArray &
return d->runInShell({"dd", {"of=" + filePath.path()}}, data);
}
enum class TransferDirection {
Upload,
Download,
Invalid
};
static TransferDirection transferDirection(const FileToTransfer &file)
{
if (file.m_source.needsDevice() == file.m_target.needsDevice())
return TransferDirection::Invalid;
return file.m_source.needsDevice() ? TransferDirection::Download : TransferDirection::Upload;
}
static TransferDirection transferDirection(const FilesToTransfer &files)
{
if (files.isEmpty())
return TransferDirection::Invalid;
const TransferDirection direction = transferDirection(files.first());
for (const FileToTransfer &file : files) {
if (transferDirection(file) != direction)
return TransferDirection::Invalid;
}
return direction;
}
static bool isDeviceMatched(const FilePath &file, const QString &id)
{
return (file.scheme() == "device") && (file.host() == id);
}
static bool isDeviceMatched(const FilesToTransfer &files, const QString &id)
{
for (const FileToTransfer &file : files) {
if (transferDirection(file) == TransferDirection::Upload && !isDeviceMatched(file.m_target, id))
return false;
if (transferDirection(file) == TransferDirection::Download && !isDeviceMatched(file.m_source, id))
return false;
}
return true;
}
static FilePaths dirsToCreate(const FilesToTransfer &files)
{
FilePaths dirs;
@@ -1474,77 +1434,33 @@ static FilePaths dirsToCreate(const FilesToTransfer &files)
return dirs;
}
static QByteArray transferCommand(const TransferDirection transferDirection, bool link)
static QByteArray transferCommand(const FileTransferDirection direction, bool link)
{
if (transferDirection == TransferDirection::Upload)
if (direction == FileTransferDirection::Upload)
return link ? "ln -s" : "put";
if (transferDirection == TransferDirection::Download)
if (direction == FileTransferDirection::Download)
return "get";
return {};
}
class FileTransferInterface : public QObject
class SshTransferInterface : public FileTransferInterface
{
Q_OBJECT
public:
void setDevice(const ProjectExplorer::IDeviceConstPtr &device)
{
m_device = device;
m_devicePrivate = nullptr;
if (m_device) {
const LinuxDevice *linuxDevice = m_device.dynamicCast<const LinuxDevice>().get();
QTC_ASSERT(linuxDevice, return);
m_devicePrivate = linuxDevice->d;
}
}
void setFilesToTransfer(const FilesToTransfer &files, TransferDirection direction)
{
m_files = files;
m_direction = direction;
}
void start()
{
if (!m_devicePrivate) {
startFailed(tr("Transferring files to/from non-linux device "
"isn't supported currently."));
return;
}
m_sshParameters = displayless(m_devicePrivate->q->sshParameters());
if (SshSettings::connectionSharingEnabled()) {
m_connecting = true;
m_connectionHandle.reset(new SshConnectionHandle(m_device->sharedFromThis()));
m_connectionHandle->setParent(this);
connect(m_connectionHandle.get(), &SshConnectionHandle::connected,
this, &FileTransferInterface::handleConnected);
connect(m_connectionHandle.get(), &SshConnectionHandle::disconnected,
this, &FileTransferInterface::handleDisconnected);
m_devicePrivate->attachToSharedConnection(m_connectionHandle.get(), m_sshParameters);
} else {
startImpl();
}
}
signals:
void progress(const QString &progressMessage);
void done(const Utils::ProcessResultData &resultData);
protected:
FileTransferInterface(FileTransferMethod method)
: m_method(method)
SshTransferInterface(const FileTransferSetupData &setup, LinuxDevicePrivate *devicePrivate)
: FileTransferInterface(setup)
, m_device(devicePrivate->q->sharedFromThis())
, m_devicePrivate(devicePrivate)
, m_process(this)
{
m_direction = m_setup.m_files.isEmpty() ? FileTransferDirection::Invalid
: m_setup.m_files.first().direction();
SshParameters::setupSshEnvironment(&m_process);
connect(&m_process, &QtcProcess::readyReadStandardOutput, this, [this] {
emit progress(QString::fromLocal8Bit(m_process.readAllStandardOutput()));
});
connect(&m_process, &QtcProcess::done, this, &FileTransferInterface::doneImpl);
}
void startFailed(const QString &errorString)
{
emit done({0, QProcess::NormalExit, QProcess::FailedToStart, errorString});
connect(&m_process, &QtcProcess::done, this, &SshTransferInterface::doneImpl);
}
bool handleError()
@@ -1552,10 +1468,10 @@ protected:
ProcessResultData resultData = m_process.resultData();
if (resultData.m_error == QProcess::FailedToStart) {
resultData.m_errorString = tr("\"%1\" failed to start: %2")
.arg(FileTransfer::transferMethodName(m_method), resultData.m_errorString);
.arg(FileTransfer::transferMethodName(m_setup.m_method), resultData.m_errorString);
} else if (resultData.m_exitStatus != QProcess::NormalExit) {
resultData.m_errorString = tr("\"%1\" crashed.")
.arg(FileTransfer::transferMethodName(m_method));
.arg(FileTransfer::transferMethodName(m_setup.m_method));
} else if (resultData.m_exitCode != 0) {
resultData.m_errorString = QString::fromLocal8Bit(m_process.readAllStandardError());
} else {
@@ -1583,14 +1499,29 @@ protected:
QString userAtHost() const { return m_sshParameters.userName() + '@' + m_sshParameters.host(); }
QtcProcess &process() { return m_process; }
FilesToTransfer m_files;
TransferDirection m_direction = TransferDirection::Invalid;
FileTransferDirection direction() const { return m_direction; }
private:
virtual void startImpl() = 0;
virtual void doneImpl() = 0;
void start() final
{
m_sshParameters = displayless(m_device->sshParameters());
if (SshSettings::connectionSharingEnabled()) {
m_connecting = true;
m_connectionHandle.reset(new SshConnectionHandle(m_device));
m_connectionHandle->setParent(this);
connect(m_connectionHandle.get(), &SshConnectionHandle::connected,
this, &SshTransferInterface::handleConnected);
connect(m_connectionHandle.get(), &SshConnectionHandle::disconnected,
this, &SshTransferInterface::handleDisconnected);
m_devicePrivate->attachToSharedConnection(m_connectionHandle.get(), m_sshParameters);
} else {
startImpl();
}
}
void handleConnected(const QString &socketFilePath)
{
m_connecting = false;
@@ -1612,20 +1543,24 @@ private:
emit done(resultData); // TODO: don't emit done() on process finished afterwards
}
FileTransferMethod m_method = FileTransferMethod::Default;
IDevice::ConstPtr m_device;
LinuxDevicePrivate *m_devicePrivate = nullptr;
std::unique_ptr<SshConnectionHandle> m_connectionHandle;
QtcProcess m_process;
QString m_socketFilePath;
SshParameters m_sshParameters;
FileTransferDirection m_direction = FileTransferDirection::Invalid; // helper
// ssh shared connection related
std::unique_ptr<SshConnectionHandle> m_connectionHandle;
QString m_socketFilePath;
bool m_connecting = false;
QtcProcess m_process;
};
class SftpTransferImpl : public FileTransferInterface
class SftpTransferImpl : public SshTransferInterface
{
public:
SftpTransferImpl() : FileTransferInterface(FileTransferMethod::Sftp) { }
SftpTransferImpl(const FileTransferSetupData &setup, LinuxDevicePrivate *devicePrivate)
: SshTransferInterface(setup, devicePrivate) { }
private:
void startImpl() final
@@ -1642,12 +1577,12 @@ private:
return;
}
const FilePaths dirs = dirsToCreate(m_files);
const FilePaths dirs = dirsToCreate(m_setup.m_files);
for (const FilePath &dir : dirs) {
if (m_direction == TransferDirection::Upload) {
if (direction() == FileTransferDirection::Upload) {
m_batchFile->write("-mkdir " + ProcessArgs::quoteArgUnix(dir.path()).toLocal8Bit()
+ '\n');
} else if (m_direction == TransferDirection::Download) {
} else if (direction() == FileTransferDirection::Download) {
if (!QDir::root().mkpath(dir.path())) {
startFailed(tr("Failed to create local directory \"%1\".")
.arg(QDir::toNativeSeparators(dir.path())));
@@ -1656,10 +1591,10 @@ private:
}
}
for (const FileToTransfer &file : m_files) {
for (const FileToTransfer &file : m_setup.m_files) {
FilePath sourceFileOrLinkTarget = file.m_source;
bool link = false;
if (m_direction == TransferDirection::Upload) {
if (direction() == FileTransferDirection::Upload) {
const QFileInfo fi(file.m_source.toFileInfo());
if (fi.isSymLink()) {
link = true;
@@ -1669,7 +1604,7 @@ private:
sourceFileOrLinkTarget.setPath(fi.dir().relativeFilePath(fi.symLinkTarget()));
}
}
m_batchFile->write(transferCommand(m_direction, link) + ' '
m_batchFile->write(transferCommand(direction(), link) + ' '
+ ProcessArgs::quoteArgUnix(sourceFileOrLinkTarget.path()).toLocal8Bit() + ' '
+ ProcessArgs::quoteArgUnix(file.m_target.path()).toLocal8Bit() + '\n');
}
@@ -1684,12 +1619,11 @@ private:
std::unique_ptr<QTemporaryFile> m_batchFile;
};
class RsyncTransferImpl : public FileTransferInterface
class RsyncTransferImpl : public SshTransferInterface
{
public:
RsyncTransferImpl(const QString &flags)
: FileTransferInterface(FileTransferMethod::Rsync)
, m_flags(flags)
RsyncTransferImpl(const FileTransferSetupData &setup, LinuxDevicePrivate *devicePrivate)
: SshTransferInterface(setup, devicePrivate)
{ }
private:
@@ -1701,7 +1635,7 @@ private:
void doneImpl() final
{
if (m_files.size() == 0 || m_currentIndex == m_files.size() - 1)
if (m_setup.m_files.size() == 0 || m_currentIndex == m_setup.m_files.size() - 1)
return handleDone();
if (handleError())
@@ -1718,10 +1652,10 @@ private:
const QString sshCmdLine = ProcessArgs::joinArgs(
QStringList{SshSettings::sshFilePath().toUserOutput()}
<< fullConnectionOptions(), OsTypeLinux);
QStringList options{"-e", sshCmdLine, m_flags};
QStringList options{"-e", sshCmdLine, m_setup.m_rsyncFlags};
if (!m_files.isEmpty()) { // NormalRun
const FileToTransfer file = m_files.at(m_currentIndex);
if (!m_setup.m_files.isEmpty()) { // NormalRun
const FileToTransfer file = m_setup.m_files.at(m_currentIndex);
const FileToTransfer fixedFile = fixLocalFileOnWindows(file, options);
const auto fixedPaths = fixPaths(fixedFile, userAtHost());
@@ -1740,7 +1674,7 @@ private:
if (!HostOsInfo::isWindowsHost())
return file;
QString localFilePath = m_direction == TransferDirection::Upload
QString localFilePath = direction() == FileTransferDirection::Upload
? file.m_source.path() : file.m_target.path();
localFilePath = '/' + localFilePath.at(0) + localFilePath.mid(2);
if (anyOf(options, [](const QString &opt) {
@@ -1749,7 +1683,7 @@ private:
}
FileToTransfer fixedFile = file;
(m_direction == TransferDirection::Upload) ? fixedFile.m_source.setPath(localFilePath)
(direction() == FileTransferDirection::Upload) ? fixedFile.m_source.setPath(localFilePath)
: fixedFile.m_target.setPath(localFilePath);
return fixedFile;
}
@@ -1758,7 +1692,7 @@ private:
{
FilePath localPath;
FilePath remotePath;
if (m_direction == TransferDirection::Upload) {
if (direction() == FileTransferDirection::Upload) {
localPath = file.m_source;
remotePath = file.m_target;
} else {
@@ -1769,166 +1703,24 @@ private:
? localPath.path() + '/' : localPath.path();
const QString remote = remoteHost + ':' + remotePath.path();
return m_direction == TransferDirection::Upload ? qMakePair(local, remote)
return direction() == FileTransferDirection::Upload ? qMakePair(local, remote)
: qMakePair(remote, local);
}
QString m_flags;
int m_currentIndex = 0;
};
class FileTransferPrivate : public QObject
FileTransferInterface *LinuxDevice::createFileTransferInterface(
const FileTransferSetupData &setup) const
{
Q_OBJECT
public:
void test() { run(TestRun); }
void start() { run(NormalRun); }
void stop();
FileTransferMethod m_method = FileTransferMethod::Default;
IDevice::ConstPtr m_device;
FilesToTransfer m_files;
QString m_rsyncFlags = FileTransfer::defaultRsyncFlags();
signals:
void progress(const QString &progressMessage);
void done(const Utils::ProcessResultData &resultData);
private:
enum RunMode {
NormalRun,
TestRun
};
void startFailed(const QString &errorString);
void run(RunMode mode);
std::unique_ptr<FileTransferInterface> m_transfer;
};
void FileTransferPrivate::stop()
{
if (!m_transfer)
return;
m_transfer->disconnect();
m_transfer.release()->deleteLater();
}
void FileTransferPrivate::startFailed(const QString &errorString)
{
emit done({0, QProcess::NormalExit, QProcess::FailedToStart, errorString});
}
void FileTransferPrivate::run(RunMode mode)
{
stop();
TransferDirection direction = TransferDirection::Invalid;
if (mode == NormalRun) {
if (m_files.isEmpty())
return startFailed(tr("No files to transfer."));
if (!m_device)
return startFailed(tr("No device set for transfer."));
direction = transferDirection(m_files);
if (direction == TransferDirection::Invalid)
return startFailed(tr("Mixing different types of transfer in one go."));
if (!isDeviceMatched(m_files, m_device->id().toString()))
return startFailed(tr("Trying to transfer into / from not matching device."));
}
switch (m_method) {
case FileTransferMethod::Sftp:
m_transfer.reset(new SftpTransferImpl());
break;
case FileTransferMethod::Rsync:
m_transfer.reset(new RsyncTransferImpl(m_rsyncFlags));
break;
}
QTC_ASSERT(m_transfer, startFailed(tr("Missing transfer implementation.")); return);
m_transfer->setParent(this);
m_transfer->setDevice(m_device);
if (mode == NormalRun)
m_transfer->setFilesToTransfer(m_files, direction);
connect(m_transfer.get(), &FileTransferInterface::progress,
this, &FileTransferPrivate::progress);
connect(m_transfer.get(), &FileTransferInterface::done,
this, &FileTransferPrivate::done);
m_transfer->start();
}
FileTransfer::FileTransfer()
: d(new FileTransferPrivate)
{
d->setParent(this);
connect(d, &FileTransferPrivate::progress, this, &FileTransfer::progress);
connect(d, &FileTransferPrivate::done, this, &FileTransfer::done);
}
FileTransfer::~FileTransfer()
{
stop();
delete d;
}
void FileTransfer::setDevice(const ProjectExplorer::IDeviceConstPtr &device)
{
d->m_device = device;
}
void FileTransfer::setTransferMethod(FileTransferMethod method)
{
d->m_method = method;
}
void FileTransfer::setFilesToTransfer(const FilesToTransfer &files)
{
d->m_files = files;
}
void FileTransfer::setRsyncFlags(const QString &flags)
{
d->m_rsyncFlags = flags;
}
void FileTransfer::test()
{
d->test();
}
FileTransferMethod FileTransfer::transferMethod() const
{
return d->m_method;
}
void FileTransfer::start()
{
d->start();
}
void FileTransfer::stop()
{
d->stop();
}
QString FileTransfer::transferMethodName(FileTransferMethod method)
{
switch (method) {
case FileTransferMethod::Sftp: return FileTransfer::tr("sftp");
case FileTransferMethod::Rsync: return FileTransfer::tr("rsync");
switch (setup.m_method) {
case FileTransferMethod::Sftp: return new SftpTransferImpl(setup, d);
case FileTransferMethod::Rsync: return new RsyncTransferImpl(setup, d);
}
QTC_CHECK(false);
return {};
}
QString FileTransfer::defaultRsyncFlags()
{
return "-av";
}
namespace Internal {
// Factory

View File

@@ -81,6 +81,8 @@ public:
bool writeFileContents(const Utils::FilePath &filePath, const QByteArray &data) const override;
QDateTime lastModified(const Utils::FilePath &filePath) const override;
Utils::ProcessInterface *createProcessInterface() const override;
ProjectExplorer::FileTransferInterface *createFileTransferInterface(
const ProjectExplorer::FileTransferSetupData &setup) const override;
Utils::Environment systemEnvironment() const override;
qint64 fileSize(const Utils::FilePath &filePath) const override;
qint64 bytesAvailable(const Utils::FilePath &filePath) const override;
@@ -92,7 +94,7 @@ protected:
class LinuxDevicePrivate *d;
friend class SshProcessInterface;
friend class FileTransferInterface;
friend class SshTransferInterface;
};
namespace Internal {

View File

@@ -25,10 +25,10 @@
#include "linuxdevicetester.h"
#include "filetransfer.h"
#include "remotelinux_constants.h"
#include <projectexplorer/devicesupport/deviceusedportsgatherer.h>
#include <projectexplorer/devicesupport/filetransfer.h>
#include <utils/algorithm.h>
#include <utils/port.h>
#include <utils/processinterface.h>
@@ -84,7 +84,6 @@ void GenericLinuxDeviceTester::testDevice(const IDevice::Ptr &deviceConfiguratio
QTC_ASSERT(d->state == Inactive, return);
d->device = deviceConfiguration;
d->fileTransfer.setDevice(d->device);
testEcho();
}
@@ -215,7 +214,7 @@ void GenericLinuxDeviceTester::testFileTransfer(FileTransferMethod method)
.arg(FileTransfer::transferMethodName(method)));
d->fileTransfer.setTransferMethod(method);
d->fileTransfer.test();
d->fileTransfer.test(d->device);
}
void GenericLinuxDeviceTester::handleFileTransferDone(const ProcessResultData &resultData)

View File

@@ -27,8 +27,7 @@
#include "remotelinux_export.h"
#include "filetransfer.h"
#include <projectexplorer/devicesupport/filetransferinterface.h>
#include <projectexplorer/devicesupport/idevice.h>
namespace Utils { class ProcessResultData; }
@@ -59,7 +58,7 @@ private:
void handlePortsGathererError(const QString &message);
void handlePortsGathererDone();
void testFileTransfer(FileTransferMethod method);
void testFileTransfer(ProjectExplorer::FileTransferMethod method);
void handleFileTransferDone(const Utils::ProcessResultData &resultData);
void setFinished(ProjectExplorer::DeviceTester::TestResult result);

View File

@@ -21,7 +21,6 @@ Project {
"abstractremotelinuxdeploystep.h",
"deploymenttimeinfo.cpp",
"deploymenttimeinfo.h",
"filetransfer.h",
"genericdirectuploadservice.cpp",
"genericdirectuploadservice.h",
"genericdirectuploadstep.cpp",

View File

@@ -26,10 +26,10 @@
#include "rsyncdeploystep.h"
#include "abstractremotelinuxdeployservice.h"
#include "filetransfer.h"
#include "remotelinux_constants.h"
#include <projectexplorer/deploymentdata.h>
#include <projectexplorer/devicesupport/filetransfer.h>
#include <projectexplorer/devicesupport/idevice.h>
#include <projectexplorer/runconfigurationaspects.h>
#include <projectexplorer/target.h>
@@ -136,7 +136,6 @@ void RsyncDeployService::createRemoteDirectories()
void RsyncDeployService::deployFiles()
{
m_fileTransfer.setDevice(deviceConfiguration());
m_fileTransfer.setTransferMethod(FileTransferMethod::Rsync);
m_fileTransfer.setRsyncFlags(m_flags);
m_fileTransfer.setFilesToTransfer(m_files);
@@ -161,7 +160,7 @@ RsyncDeployStep::RsyncDeployStep(BuildStepList *bsl, Utils::Id id)
flags->setDisplayStyle(StringAspect::LineEditDisplay);
flags->setSettingsKey("RemoteLinux.RsyncDeployStep.Flags");
flags->setLabelText(tr("Flags:"));
flags->setValue(FileTransfer::defaultRsyncFlags());
flags->setValue(FileTransferSetupData::defaultRsyncFlags());
auto ignoreMissingFiles = addAspect<BoolAspect>();
ignoreMissingFiles->setSettingsKey("RemoteLinux.RsyncDeployStep.IgnoreMissingFiles");

View File

@@ -25,12 +25,12 @@
#include "uploadandinstalltarpackagestep.h"
#include "filetransfer.h"
#include "remotelinux_constants.h"
#include "remotelinuxpackageinstaller.h"
#include "tarpackagecreationstep.h"
#include <projectexplorer/deployconfiguration.h>
#include <projectexplorer/devicesupport/filetransfer.h>
#include <projectexplorer/devicesupport/idevice.h>
#include <utils/processinterface.h>
@@ -103,7 +103,6 @@ void UploadAndInstallTarPackageService::doDeploy()
const QString remoteFilePath = uploadDir() + QLatin1Char('/') + m_packageFilePath.fileName();
const FilesToTransfer files {{m_packageFilePath,
deviceConfiguration()->filePath(remoteFilePath)}};
m_uploader.setDevice(deviceConfiguration());
m_uploader.setFilesToTransfer(files);
m_uploader.start();
}