diff options
Diffstat (limited to 'src')
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 |