summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/libs/ssh/sshconnection.cpp14
-rw-r--r--src/libs/ssh/sshconnection.h1
-rw-r--r--src/plugins/projectexplorer/devicesupport/devicesettingswidget.cpp2
-rw-r--r--src/plugins/projectexplorer/devicesupport/devicetestdialog.cpp2
-rw-r--r--src/plugins/projectexplorer/devicesupport/devicetestdialog.h2
-rw-r--r--src/plugins/projectexplorer/devicesupport/idevice.cpp14
-rw-r--r--src/plugins/projectexplorer/devicesupport/idevice.h5
-rw-r--r--src/plugins/qnx/qnxdevicetester.cpp2
-rw-r--r--src/plugins/qnx/qnxdevicetester.h2
-rw-r--r--src/plugins/remotelinux/linuxdevice.cpp10
-rw-r--r--src/plugins/remotelinux/linuxdevice.h3
-rw-r--r--src/plugins/remotelinux/linuxdevicetester.cpp59
-rw-r--r--src/plugins/remotelinux/linuxdevicetester.h3
-rw-r--r--src/plugins/remotelinux/remotelinux.pro2
-rw-r--r--src/plugins/remotelinux/remotelinux.qbs2
-rw-r--r--src/plugins/remotelinux/remotelinuxdeployconfiguration.cpp10
-rw-r--r--src/plugins/remotelinux/remotelinuxplugin.cpp2
-rw-r--r--src/plugins/remotelinux/rsyncdeploystep.cpp236
-rw-r--r--src/plugins/remotelinux/rsyncdeploystep.h65
19 files changed, 421 insertions, 15 deletions
diff --git a/src/libs/ssh/sshconnection.cpp b/src/libs/ssh/sshconnection.cpp
index 17108b91cc..c8b8e23322 100644
--- a/src/libs/ssh/sshconnection.cpp
+++ b/src/libs/ssh/sshconnection.cpp
@@ -111,7 +111,7 @@ struct SshConnection::SshConnectionPrivate
return masterSocketDir->path() + "/control_socket";
}
- QStringList connectionArgs() const
+ QStringList connectionOptions() const
{
QString hostKeyCheckingString;
switch (connParams.hostKeyCheckingMode) {
@@ -137,7 +137,12 @@ struct SshConnection::SshConnectionPrivate
args << "-o" << ("ControlPath=" + socketFilePath());
if (connParams.timeout != 0)
args << "-o" << ("ConnectTimeout=" + QString::number(connParams.timeout));
- return args << connParams.host();
+ return args;
+ }
+
+ QStringList connectionArgs() const
+ {
+ return connectionOptions() << connParams.host();
}
SshConnectionParameters connParams;
@@ -289,6 +294,11 @@ SshConnectionInfo SshConnection::connectionInfo() const
return d->connInfo;
}
+QStringList SshConnection::connectionOptions() const
+{
+ return d->connectionOptions();
+}
+
bool SshConnection::sharingEnabled() const
{
return d->sharingEnabled;
diff --git a/src/libs/ssh/sshconnection.h b/src/libs/ssh/sshconnection.h
index 09151795d0..1b5775682e 100644
--- a/src/libs/ssh/sshconnection.h
+++ b/src/libs/ssh/sshconnection.h
@@ -107,6 +107,7 @@ public:
QString errorString() const;
SshConnectionParameters connectionParameters() const;
SshConnectionInfo connectionInfo() const;
+ QStringList connectionOptions() const;
bool sharingEnabled() const;
~SshConnection();
diff --git a/src/plugins/projectexplorer/devicesupport/devicesettingswidget.cpp b/src/plugins/projectexplorer/devicesupport/devicesettingswidget.cpp
index e837d5121e..4cd0f8a490 100644
--- a/src/plugins/projectexplorer/devicesupport/devicesettingswidget.cpp
+++ b/src/plugins/projectexplorer/devicesupport/devicesettingswidget.cpp
@@ -256,7 +256,7 @@ void DeviceSettingsWidget::testDevice()
{
const IDevice::ConstPtr &device = currentDevice();
QTC_ASSERT(device && device->hasDeviceTester(), return);
- DeviceTestDialog dlg(device, this);
+ DeviceTestDialog dlg(m_deviceManager->mutableDevice(device->id()), this);
dlg.exec();
}
diff --git a/src/plugins/projectexplorer/devicesupport/devicetestdialog.cpp b/src/plugins/projectexplorer/devicesupport/devicetestdialog.cpp
index e308b76f46..5c60e5e70b 100644
--- a/src/plugins/projectexplorer/devicesupport/devicetestdialog.cpp
+++ b/src/plugins/projectexplorer/devicesupport/devicetestdialog.cpp
@@ -48,7 +48,7 @@ public:
bool finished;
};
-DeviceTestDialog::DeviceTestDialog(const IDevice::ConstPtr &deviceConfiguration,
+DeviceTestDialog::DeviceTestDialog(const IDevice::Ptr &deviceConfiguration,
QWidget *parent)
: QDialog(parent)
, d(std::make_unique<DeviceTestDialogPrivate>(deviceConfiguration->createDeviceTester()))
diff --git a/src/plugins/projectexplorer/devicesupport/devicetestdialog.h b/src/plugins/projectexplorer/devicesupport/devicetestdialog.h
index 7263234fbe..d7a25c1dcd 100644
--- a/src/plugins/projectexplorer/devicesupport/devicetestdialog.h
+++ b/src/plugins/projectexplorer/devicesupport/devicetestdialog.h
@@ -39,7 +39,7 @@ class DeviceTestDialog : public QDialog
Q_OBJECT
public:
- DeviceTestDialog(const IDevice::ConstPtr &deviceConfiguration, QWidget *parent = nullptr);
+ DeviceTestDialog(const IDevice::Ptr &deviceConfiguration, QWidget *parent = nullptr);
~DeviceTestDialog() override;
void reject() override;
diff --git a/src/plugins/projectexplorer/devicesupport/idevice.cpp b/src/plugins/projectexplorer/devicesupport/idevice.cpp
index 0487954312..4d17ff933d 100644
--- a/src/plugins/projectexplorer/devicesupport/idevice.cpp
+++ b/src/plugins/projectexplorer/devicesupport/idevice.cpp
@@ -112,6 +112,7 @@ const char IdKey[] = "InternalId";
const char OriginKey[] = "Origin";
const char MachineTypeKey[] = "Type";
const char VersionKey[] = "Version";
+const char ExtraDataKey[] = "ExtraData";
// Connection
const char HostKey[] = "Host";
@@ -152,6 +153,7 @@ public:
QString qmlsceneCommand;
QList<Utils::Icon> deviceIcons;
+ QVariantHash extraData;
};
} // namespace Internal
@@ -347,6 +349,7 @@ void IDevice::fromMap(const QVariantMap &map)
d->debugServerPath = map.value(QLatin1String(DebugServerKey)).toString();
d->qmlsceneCommand = map.value(QLatin1String(QmlsceneKey)).toString();
+ d->extraData = map.value(ExtraDataKey).toHash();
}
/*!
@@ -377,6 +380,7 @@ QVariantMap IDevice::toMap() const
map.insert(QLatin1String(DebugServerKey), d->debugServerPath);
map.insert(QLatin1String(QmlsceneKey), d->qmlsceneCommand);
+ map.insert(ExtraDataKey, d->extraData);
return map;
}
@@ -446,6 +450,16 @@ void IDevice::setQmlsceneCommand(const QString &path)
d->qmlsceneCommand = path;
}
+void IDevice::setExtraData(Core::Id kind, const QVariant &data)
+{
+ d->extraData.insert(kind.toString(), data);
+}
+
+QVariant IDevice::extraData(Core::Id kind) const
+{
+ return d->extraData.value(kind.toString());
+}
+
int IDevice::version() const
{
return d->version;
diff --git a/src/plugins/projectexplorer/devicesupport/idevice.h b/src/plugins/projectexplorer/devicesupport/idevice.h
index 4d3a2f8302..f6cfaef574 100644
--- a/src/plugins/projectexplorer/devicesupport/idevice.h
+++ b/src/plugins/projectexplorer/devicesupport/idevice.h
@@ -206,6 +206,9 @@ public:
QString qmlsceneCommand() const;
void setQmlsceneCommand(const QString &path);
+ void setExtraData(Core::Id kind, const QVariant &data);
+ QVariant extraData(Core::Id kind) const;
+
protected:
IDevice();
IDevice(Core::Id type, Origin origin, MachineType machineType, Core::Id id = Core::Id());
@@ -226,7 +229,7 @@ class PROJECTEXPLORER_EXPORT DeviceTester : public QObject
public:
enum TestResult { TestSuccess, TestFailure };
- virtual void testDevice(const ProjectExplorer::IDevice::ConstPtr &deviceConfiguration) = 0;
+ virtual void testDevice(const ProjectExplorer::IDevice::Ptr &deviceConfiguration) = 0;
virtual void stopTest() = 0;
signals:
diff --git a/src/plugins/qnx/qnxdevicetester.cpp b/src/plugins/qnx/qnxdevicetester.cpp
index 004de59acb..cf5614e817 100644
--- a/src/plugins/qnx/qnxdevicetester.cpp
+++ b/src/plugins/qnx/qnxdevicetester.cpp
@@ -66,7 +66,7 @@ QnxDeviceTester::QnxDeviceTester(QObject *parent)
<< QLatin1String("uname");
}
-void QnxDeviceTester::testDevice(const ProjectExplorer::IDevice::ConstPtr &deviceConfiguration)
+void QnxDeviceTester::testDevice(const ProjectExplorer::IDevice::Ptr &deviceConfiguration)
{
QTC_ASSERT(m_state == Inactive, return);
diff --git a/src/plugins/qnx/qnxdevicetester.h b/src/plugins/qnx/qnxdevicetester.h
index 91d5548bda..c63ec13d75 100644
--- a/src/plugins/qnx/qnxdevicetester.h
+++ b/src/plugins/qnx/qnxdevicetester.h
@@ -40,7 +40,7 @@ class QnxDeviceTester : public ProjectExplorer::DeviceTester
public:
explicit QnxDeviceTester(QObject *parent = nullptr);
- void testDevice(const ProjectExplorer::IDevice::ConstPtr &deviceConfiguration) override;
+ void testDevice(const ProjectExplorer::IDevice::Ptr &deviceConfiguration) override;
void stopTest() override;
private slots:
diff --git a/src/plugins/remotelinux/linuxdevice.cpp b/src/plugins/remotelinux/linuxdevice.cpp
index 3b62402a80..426a12e04c 100644
--- a/src/plugins/remotelinux/linuxdevice.cpp
+++ b/src/plugins/remotelinux/linuxdevice.cpp
@@ -286,4 +286,14 @@ DeviceEnvironmentFetcher::Ptr LinuxDevice::environmentFetcher() const
return DeviceEnvironmentFetcher::Ptr(new LinuxDeviceEnvironmentFetcher(sharedFromThis()));
}
+void LinuxDevice::setSupportsRsync(bool supportsRsync)
+{
+ setExtraData("RemoteLinux.SupportsRSync", supportsRsync);
+}
+
+bool LinuxDevice::supportsRSync() const
+{
+ return extraData("RemoteLinux.SupportsRSync").toBool();
+}
+
} // namespace RemoteLinux
diff --git a/src/plugins/remotelinux/linuxdevice.h b/src/plugins/remotelinux/linuxdevice.h
index 210bb6177f..919ccb092e 100644
--- a/src/plugins/remotelinux/linuxdevice.h
+++ b/src/plugins/remotelinux/linuxdevice.h
@@ -65,6 +65,9 @@ public:
ProjectExplorer::DeviceProcessSignalOperation::Ptr signalOperation() const override;
ProjectExplorer::DeviceEnvironmentFetcher::Ptr environmentFetcher() const override;
+ void setSupportsRsync(bool supportsRsync);
+ bool supportsRSync() const;
+
protected:
LinuxDevice() = default;
LinuxDevice(const QString &name, Core::Id type,
diff --git a/src/plugins/remotelinux/linuxdevicetester.cpp b/src/plugins/remotelinux/linuxdevicetester.cpp
index 4ef601374c..b6a470ab07 100644
--- a/src/plugins/remotelinux/linuxdevicetester.cpp
+++ b/src/plugins/remotelinux/linuxdevicetester.cpp
@@ -25,6 +25,9 @@
#include "linuxdevicetester.h"
+#include "linuxdevice.h"
+#include "rsyncdeploystep.h"
+
#include <projectexplorer/devicesupport/deviceusedportsgatherer.h>
#include <utils/port.h>
#include <utils/qtcassert.h>
@@ -40,19 +43,21 @@ namespace RemoteLinux {
namespace Internal {
namespace {
-enum State { Inactive, Connecting, RunningUname, TestingPorts, TestingSftp };
+enum State { Inactive, Connecting, RunningUname, TestingPorts, TestingSftp, TestingRsync };
} // anonymous namespace
class GenericLinuxDeviceTesterPrivate
{
public:
- IDevice::ConstPtr deviceConfiguration;
+ IDevice::Ptr deviceConfiguration;
SshConnection *connection = nullptr;
SshRemoteProcessPtr process;
DeviceUsedPortsGatherer portsGatherer;
SftpTransferPtr sftpUpload;
+ QProcess rsyncProcess;
State state = Inactive;
+ bool sftpWorks = false;
};
} // namespace Internal
@@ -71,7 +76,7 @@ GenericLinuxDeviceTester::~GenericLinuxDeviceTester()
delete d;
}
-void GenericLinuxDeviceTester::testDevice(const IDevice::ConstPtr &deviceConfiguration)
+void GenericLinuxDeviceTester::testDevice(const IDevice::Ptr &deviceConfiguration)
{
QTC_ASSERT(d->state == Inactive, return);
@@ -105,6 +110,9 @@ void GenericLinuxDeviceTester::stopTest()
case TestingSftp:
d->sftpUpload->stop();
break;
+ case TestingRsync:
+ d->rsyncProcess.disconnect();
+ d->rsyncProcess.kill();
case Inactive:
break;
}
@@ -195,11 +203,52 @@ void GenericLinuxDeviceTester::handleSftpFinished(const QString &error)
QTC_ASSERT(d->state == TestingSftp, return);
if (!error.isEmpty()) {
emit errorMessage(tr("Error setting up SFTP connection: %1\n").arg(error));
- setFinished(TestFailure);
+ d->sftpWorks = false;
} else {
emit progressMessage(tr("SFTP service available.\n"));
- setFinished(TestSuccess);
+ d->sftpWorks = true;
+ }
+
+ emit progressMessage(tr("Checking whether rsync works..."));
+ connect(&d->rsyncProcess, &QProcess::errorOccurred, [this] {
+ if (d->rsyncProcess.error() == QProcess::FailedToStart)
+ handleRsyncFinished();
+ });
+ connect(&d->rsyncProcess, static_cast<void (QProcess::*)(int)>(&QProcess::finished),
+ [this] {
+ handleRsyncFinished();
+ });
+ const RsyncCommandLine cmdLine = RsyncDeployStep::rsyncCommand(*d->connection);
+ const QStringList args = QStringList(cmdLine.options)
+ << "-n" << "--exclude=*" << (cmdLine.remoteHostSpec + ":/tmp");
+ d->rsyncProcess.start("rsync", args);
+}
+
+void GenericLinuxDeviceTester::handleRsyncFinished()
+{
+ QString error;
+ if (d->rsyncProcess.error() == QProcess::FailedToStart) {
+ error = tr("Failed to start rsync: %1\n").arg(d->rsyncProcess.errorString());
+ } else if (d->rsyncProcess.exitStatus() == QProcess::CrashExit) {
+ error = tr("rsync crashed.\n");
+ } else if (d->rsyncProcess.exitCode() != 0) {
+ error = tr("rsync failed with exit code %1: %2\n")
+ .arg(d->rsyncProcess.exitCode())
+ .arg(QString::fromLocal8Bit(d->rsyncProcess.readAllStandardError()));
}
+ TestResult result = TestSuccess;
+ if (!error.isEmpty()) {
+ emit errorMessage(error);
+ if (!d->sftpWorks) {
+ emit errorMessage(tr("Deployment to this device will not work out of the box.\n"));
+ result = TestFailure;
+ }
+ } else {
+ emit progressMessage(tr("rsync is functional.\n"));
+ }
+
+ d->deviceConfiguration.staticCast<LinuxDevice>()->setSupportsRsync(error.isEmpty());
+ setFinished(result);
}
void GenericLinuxDeviceTester::setFinished(TestResult result)
diff --git a/src/plugins/remotelinux/linuxdevicetester.h b/src/plugins/remotelinux/linuxdevicetester.h
index a63ec51d04..9aefdda33f 100644
--- a/src/plugins/remotelinux/linuxdevicetester.h
+++ b/src/plugins/remotelinux/linuxdevicetester.h
@@ -41,7 +41,7 @@ public:
explicit GenericLinuxDeviceTester(QObject *parent = nullptr);
~GenericLinuxDeviceTester() override;
- void testDevice(const ProjectExplorer::IDevice::ConstPtr &deviceConfiguration) override;
+ void testDevice(const ProjectExplorer::IDevice::Ptr &deviceConfiguration) override;
void stopTest() override;
private:
@@ -51,6 +51,7 @@ private:
void handlePortsGatheringError(const QString &message);
void handlePortListReady();
void handleSftpFinished(const QString &error);
+ void handleRsyncFinished();
void setFinished(ProjectExplorer::DeviceTester::TestResult result);
Internal::GenericLinuxDeviceTesterPrivate * const d;
diff --git a/src/plugins/remotelinux/remotelinux.pro b/src/plugins/remotelinux/remotelinux.pro
index a5aa037ef1..027cc59b22 100644
--- a/src/plugins/remotelinux/remotelinux.pro
+++ b/src/plugins/remotelinux/remotelinux.pro
@@ -40,6 +40,7 @@ HEADERS += \
remotelinuxkillappservice.h \
remotelinuxkillappstep.h \
remotelinuxqmltoolingsupport.h \
+ rsyncdeploystep.h \
linuxdeviceprocess.h \
remotelinuxcustomrunconfiguration.h \
remotelinuxsignaloperation.h \
@@ -82,6 +83,7 @@ SOURCES += \
remotelinuxkillappservice.cpp \
remotelinuxkillappstep.cpp \
remotelinuxqmltoolingsupport.cpp \
+ rsyncdeploystep.cpp \
linuxdeviceprocess.cpp \
remotelinuxcustomrunconfiguration.cpp \
remotelinuxsignaloperation.cpp \
diff --git a/src/plugins/remotelinux/remotelinux.qbs b/src/plugins/remotelinux/remotelinux.qbs
index 7d85832215..1f553d662b 100644
--- a/src/plugins/remotelinux/remotelinux.qbs
+++ b/src/plugins/remotelinux/remotelinux.qbs
@@ -95,6 +95,8 @@ Project {
"remotelinuxsignaloperation.h",
"remotelinuxx11forwardingaspect.cpp",
"remotelinuxx11forwardingaspect.h",
+ "rsyncdeploystep.cpp",
+ "rsyncdeploystep.h",
"sshkeydeployer.cpp",
"sshkeydeployer.h",
"tarpackagecreationstep.cpp",
diff --git a/src/plugins/remotelinux/remotelinuxdeployconfiguration.cpp b/src/plugins/remotelinux/remotelinuxdeployconfiguration.cpp
index c9a778f4c0..c452bfc374 100644
--- a/src/plugins/remotelinux/remotelinuxdeployconfiguration.cpp
+++ b/src/plugins/remotelinux/remotelinuxdeployconfiguration.cpp
@@ -26,12 +26,15 @@
#include "remotelinuxdeployconfiguration.h"
#include "genericdirectuploadstep.h"
+#include "linuxdevice.h"
#include "remotelinuxcheckforfreediskspacestep.h"
#include "remotelinuxkillappstep.h"
#include "remotelinux_constants.h"
+#include "rsyncdeploystep.h"
#include <projectexplorer/abi.h>
#include <projectexplorer/deploymentdataview.h>
+#include <projectexplorer/kitinformation.h>
#include <projectexplorer/project.h>
#include <projectexplorer/target.h>
@@ -51,7 +54,12 @@ void RemoteLinuxDeployConfiguration::initialize()
{
stepList()->appendStep(new RemoteLinuxCheckForFreeDiskSpaceStep(stepList()));
stepList()->appendStep(new RemoteLinuxKillAppStep(stepList()));
- stepList()->appendStep(new GenericDirectUploadStep(stepList()));
+ const LinuxDevice::ConstPtr device = DeviceKitInformation::device(target()->kit())
+ .staticCast<const LinuxDevice>();
+ if (device && device->supportsRSync())
+ stepList()->appendStep(new RsyncDeployStep(stepList()));
+ else
+ stepList()->appendStep(new GenericDirectUploadStep(stepList()));
}
NamedWidget *RemoteLinuxDeployConfiguration::createConfigWidget()
diff --git a/src/plugins/remotelinux/remotelinuxplugin.cpp b/src/plugins/remotelinux/remotelinuxplugin.cpp
index 6a5dffa747..f0f9b7777d 100644
--- a/src/plugins/remotelinux/remotelinuxplugin.cpp
+++ b/src/plugins/remotelinux/remotelinuxplugin.cpp
@@ -39,6 +39,7 @@
#include "remotelinuxdeployconfiguration.h"
#include "remotelinuxcustomcommanddeploymentstep.h"
#include "remotelinuxkillappstep.h"
+#include "rsyncdeploystep.h"
#include "tarpackagecreationstep.h"
#include "uploadandinstalltarpackagestep.h"
@@ -73,6 +74,7 @@ public:
GenericDeployStepFactory<TarPackageCreationStep> tarPackageCreationStepFactory;
GenericDeployStepFactory<UploadAndInstallTarPackageStep> uploadAndInstallTarPackageStepFactory;
GenericDeployStepFactory<GenericDirectUploadStep> genericDirectUploadStepFactory;
+ GenericDeployStepFactory<RsyncDeployStep> rsyncDeployStepFactory;
GenericDeployStepFactory<RemoteLinuxCustomCommandDeploymentStep>
customCommandDeploymentStepFactory;
GenericDeployStepFactory<RemoteLinuxCheckForFreeDiskSpaceStep>
diff --git a/src/plugins/remotelinux/rsyncdeploystep.cpp b/src/plugins/remotelinux/rsyncdeploystep.cpp
new file mode 100644
index 0000000000..eca82f765c
--- /dev/null
+++ b/src/plugins/remotelinux/rsyncdeploystep.cpp
@@ -0,0 +1,236 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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 "rsyncdeploystep.h"
+
+#include "abstractremotelinuxdeployservice.h"
+
+#include <projectexplorer/deploymentdata.h>
+#include <projectexplorer/runconfigurationaspects.h>
+#include <projectexplorer/target.h>
+#include <ssh/sshconnection.h>
+#include <ssh/sshremoteprocess.h>
+#include <ssh/sshsettings.h>
+#include <utils/algorithm.h>
+#include <utils/qtcprocess.h>
+
+using namespace ProjectExplorer;
+using namespace QSsh;
+using namespace Utils;
+
+namespace RemoteLinux {
+namespace Internal {
+
+class RsyncDeployService : public AbstractRemoteLinuxDeployService
+{
+ Q_OBJECT
+public:
+ RsyncDeployService(QObject *parent = nullptr) : AbstractRemoteLinuxDeployService(parent) {}
+
+ void setDeployableFiles(const QList<DeployableFile> &files) { m_deployableFiles = files; }
+ void setIgnoreMissingFiles(bool ignore) { m_ignoreMissingFiles = ignore; }
+
+private:
+ bool isDeploymentNecessary() const override;
+
+ void doDeviceSetup() override { handleDeviceSetupDone(true); }
+ void stopDeviceSetup() override { handleDeviceSetupDone(false); };
+
+ void doDeploy() override;
+ void stopDeployment() override { setFinished(); };
+
+ void filterDeployableFiles() const;
+ void createRemoteDirectories();
+ void deployFiles();
+ void deployNextFile();
+ void setFinished();
+
+ mutable QList<DeployableFile> m_deployableFiles;
+ bool m_ignoreMissingFiles = false;
+ QProcess m_rsync;
+ SshRemoteProcessPtr m_mkdir;
+};
+
+bool RsyncDeployService::isDeploymentNecessary() const
+{
+ filterDeployableFiles();
+ return !m_deployableFiles.empty();
+}
+
+void RsyncDeployService::doDeploy()
+{
+ createRemoteDirectories();
+}
+
+void RsyncDeployService::filterDeployableFiles() const
+{
+ if (m_ignoreMissingFiles) {
+ erase(m_deployableFiles, [](const DeployableFile &f) {
+ return !f.localFilePath().exists();
+ });
+ }
+}
+
+void RsyncDeployService::createRemoteDirectories()
+{
+ QStringList remoteDirs;
+ for (const DeployableFile &f : m_deployableFiles)
+ remoteDirs << f.remoteDirectory();
+ remoteDirs.sort();
+ remoteDirs.removeDuplicates();
+ m_mkdir = connection()->createRemoteProcess("mkdir -p " + QtcProcess::Arguments
+ ::createUnixArgs(remoteDirs).toString().toUtf8());
+ connect(m_mkdir.get(), &SshRemoteProcess::done, [this](const QString &error) {
+ QString userError;
+ if (!error.isEmpty())
+ userError = error;
+ if (m_mkdir->exitCode() != 0)
+ userError = QString::fromUtf8(m_mkdir->readAllStandardError());
+ if (!userError.isEmpty()) {
+ emit errorMessage(tr("Failed to create remote directories: %1").arg(userError));
+ setFinished();
+ return;
+ }
+ deployFiles();
+ });
+ m_mkdir->start();
+}
+
+void RsyncDeployService::deployFiles()
+{
+ connect(&m_rsync, &QProcess::readyReadStandardOutput, [this] {
+ emit progressMessage(QString::fromLocal8Bit(m_rsync.readAllStandardOutput()));
+ });
+ connect(&m_rsync, &QProcess::readyReadStandardError, [this] {
+ emit warningMessage(QString::fromLocal8Bit(m_rsync.readAllStandardError()));
+ });
+ connect(&m_rsync, &QProcess::errorOccurred, [this] {
+ if (m_rsync.error() == QProcess::FailedToStart) {
+ emit errorMessage(tr("rsync failed to start: %1").arg(m_rsync.errorString()));
+ setFinished();
+ }
+ });
+ connect(&m_rsync, static_cast<void (QProcess::*)(int)>(&QProcess::finished), [this] {
+ if (m_rsync.exitStatus() == QProcess::CrashExit) {
+ emit errorMessage(tr("rsync crashed."));
+ setFinished();
+ return;
+ }
+ if (m_rsync.exitCode() != 0) {
+ emit errorMessage(tr("rsync failed with exit code %1.").arg(m_rsync.exitCode()));
+ setFinished();
+ return;
+ }
+ deployNextFile();
+ });
+ deployNextFile();
+}
+
+void RsyncDeployService::deployNextFile()
+{
+ if (m_deployableFiles.empty()) {
+ setFinished();
+ return;
+ }
+ const DeployableFile file = m_deployableFiles.takeFirst();
+ const RsyncCommandLine cmdLine = RsyncDeployStep::rsyncCommand(*connection());
+ const QStringList args = QStringList(cmdLine.options)
+ << file.localFilePath().toString()
+ << (cmdLine.remoteHostSpec + ':' + file.remoteFilePath());
+ m_rsync.start("rsync", args); // TODO: Get rsync location from settings?
+}
+
+void RsyncDeployService::setFinished()
+{
+ if (m_mkdir) {
+ m_mkdir->disconnect();
+ m_mkdir->kill();
+ }
+ m_rsync.disconnect();
+ m_rsync.kill();
+ handleDeploymentDone();
+}
+
+} // namespace Internal
+
+class RsyncDeployStep::RsyncDeployStepPrivate
+{
+public:
+ Internal::RsyncDeployService deployService;
+ BaseBoolAspect *ignoreMissingFilesAspect;
+};
+
+RsyncDeployStep::RsyncDeployStep(BuildStepList *bsl)
+ : AbstractRemoteLinuxDeployStep(bsl, stepId()), d(new RsyncDeployStepPrivate)
+{
+ d->ignoreMissingFilesAspect = addAspect<BaseBoolAspect>();
+ d->ignoreMissingFilesAspect
+ ->setSettingsKey("RemoteLinux.RsyncDeployStep.IgnoreMissingFiles");
+ d->ignoreMissingFilesAspect->setLabel(tr("Ignore missing files"));
+ d->ignoreMissingFilesAspect->setValue(false);
+
+ setDefaultDisplayName(displayName());
+}
+
+RsyncDeployStep::~RsyncDeployStep()
+{
+ delete d;
+}
+
+bool RsyncDeployStep::initInternal(QString *error)
+{
+ d->deployService.setDeployableFiles(target()->deploymentData().allFiles());
+ d->deployService.setIgnoreMissingFiles(d->ignoreMissingFilesAspect->value());
+ return d->deployService.isDeploymentPossible(error);
+}
+
+AbstractRemoteLinuxDeployService *RsyncDeployStep::deployService() const
+{
+ return &d->deployService;
+}
+
+Core::Id RsyncDeployStep::stepId()
+{
+ return "RemoteLinux.RsyncDeployStep";
+}
+
+QString RsyncDeployStep::displayName()
+{
+ return tr("Deploy files via rsync");
+}
+
+RsyncCommandLine RsyncDeployStep::rsyncCommand(const SshConnection &sshConnection)
+{
+ const QString sshCmdLine = QtcProcess::joinArgs(
+ QStringList{SshSettings::sshFilePath().toUserOutput()}
+ << sshConnection.connectionOptions());
+ const SshConnectionParameters sshParams = sshConnection.connectionParameters();
+ return RsyncCommandLine(QStringList{"-e", sshCmdLine, "-avz"},
+ sshParams.userName() + '@' + sshParams.host());
+}
+
+} //namespace RemoteLinux
+
+#include <rsyncdeploystep.moc>
diff --git a/src/plugins/remotelinux/rsyncdeploystep.h b/src/plugins/remotelinux/rsyncdeploystep.h
new file mode 100644
index 0000000000..ddc627e7dc
--- /dev/null
+++ b/src/plugins/remotelinux/rsyncdeploystep.h
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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 "abstractremotelinuxdeploystep.h"
+#include "remotelinux_export.h"
+
+namespace QSsh { class SshConnection; }
+
+namespace RemoteLinux {
+
+class RsyncCommandLine
+{
+public:
+ RsyncCommandLine(const QStringList &o, const QString &h) : options(o), remoteHostSpec(h) {}
+ const QStringList options;
+ const QString remoteHostSpec;
+};
+
+class REMOTELINUX_EXPORT RsyncDeployStep : public AbstractRemoteLinuxDeployStep
+{
+ Q_OBJECT
+
+public:
+ explicit RsyncDeployStep(ProjectExplorer::BuildStepList *bsl);
+ ~RsyncDeployStep() override;
+
+ static Core::Id stepId();
+ static QString displayName();
+
+ static RsyncCommandLine rsyncCommand(const QSsh::SshConnection &sshConnection);
+
+private:
+ AbstractRemoteLinuxDeployService *deployService() const override;
+
+ bool initInternal(QString *error = nullptr) override;
+
+ class RsyncDeployStepPrivate;
+ RsyncDeployStepPrivate * const d;
+};
+
+} // namespace RemoteLinux