diff --git a/src/plugins/remotelinux/rsyncdeploystep.cpp b/src/plugins/remotelinux/rsyncdeploystep.cpp index 9a407c03da7..67ddfdb5fef 100644 --- a/src/plugins/remotelinux/rsyncdeploystep.cpp +++ b/src/plugins/remotelinux/rsyncdeploystep.cpp @@ -26,6 +26,7 @@ #include "rsyncdeploystep.h" #include "abstractremotelinuxdeployservice.h" +#include "filetransfer.h" #include "remotelinux_constants.h" #include @@ -35,6 +36,7 @@ #include #include #include +#include #include using namespace ProjectExplorer; @@ -49,9 +51,34 @@ class RsyncDeployService : public AbstractRemoteLinuxDeployService Q_OBJECT public: RsyncDeployService(QObject *parent = nullptr) : AbstractRemoteLinuxDeployService(parent) - { SshConnectionParameters::setupSshEnvironment(&m_rsync); } + { + connect(&m_mkdir, &QtcProcess::done, this, [this] { + if (m_mkdir.result() != ProcessResult::FinishedWithSuccess) { + emit errorMessage(tr("Failed to create remote directories: %1").arg(m_mkdir.stdErr())); + setFinished(); + return; + } + deployFiles(); + }); + connect(&m_fileTransfer, &FileTransfer::progress, + this, &AbstractRemoteLinuxDeployService::stdOutData); + connect(&m_fileTransfer, &FileTransfer::done, this, [this](const ProcessResultData &result) { + auto notifyError = [this](const QString &message) { + emit errorMessage(message); + setFinished(); + }; + if (result.m_error == QProcess::FailedToStart) + notifyError(tr("rsync failed to start: %1").arg(result.m_errorString)); + else if (result.m_exitStatus == QProcess::CrashExit) + notifyError(tr("rsync crashed.")); + else if (result.m_exitCode != 0) + notifyError(tr("rsync failed with exit code %1.").arg(result.m_exitCode)); + else + setFinished(); + }); + } - void setDeployableFiles(const QList &files) { m_deployableFiles = files; } + void setDeployableFiles(const QList &files); void setIgnoreMissingFiles(bool ignore) { m_ignoreMissingFiles = ignore; } void setFlags(const QString &flags) { m_flags = flags; } @@ -61,23 +88,28 @@ private: void doDeploy() override; void stopDeployment() override { setFinished(); }; - void filterDeployableFiles() const; + void filterFiles() const; void createRemoteDirectories(); void deployFiles(); - void deployNextFile(); void setFinished(); - mutable QList m_deployableFiles; + mutable FilesToTransfer m_files; bool m_ignoreMissingFiles = false; QString m_flags; - QtcProcess m_rsync; - std::unique_ptr m_mkdir; + QtcProcess m_mkdir; + FileTransfer m_fileTransfer; }; +void RsyncDeployService::setDeployableFiles(const QList &files) +{ + for (const DeployableFile &f : files) + m_files.append({f.localFilePath(), deviceConfiguration()->filePath(f.remoteFilePath())}); +} + bool RsyncDeployService::isDeploymentNecessary() const { - filterDeployableFiles(); - return !m_deployableFiles.empty(); + filterFiles(); + return !m_files.empty(); } void RsyncDeployService::doDeploy() @@ -85,100 +117,46 @@ void RsyncDeployService::doDeploy() createRemoteDirectories(); } -void RsyncDeployService::filterDeployableFiles() const +void RsyncDeployService::filterFiles() const { - if (m_ignoreMissingFiles) { - Utils::erase(m_deployableFiles, [](const DeployableFile &f) { - return !f.localFilePath().exists(); - }); - } + if (!m_ignoreMissingFiles) + return; + + Utils::erase(m_files, [](const FileToTransfer &file) { return !file.m_source.exists(); }); } void RsyncDeployService::createRemoteDirectories() { QStringList remoteDirs; - for (const DeployableFile &f : qAsConst(m_deployableFiles)) - remoteDirs << f.remoteDirectory(); + for (const FileToTransfer &file : qAsConst(m_files)) + remoteDirs << file.m_target.parentDir().path(); remoteDirs.sort(); remoteDirs.removeDuplicates(); - m_mkdir.reset(new QtcProcess); - m_mkdir->setCommand({deviceConfiguration()->filePath("mkdir"), + m_mkdir.setCommand({deviceConfiguration()->filePath("mkdir"), {"-p", ProcessArgs::createUnixArgs(remoteDirs).toString()}}); - connect(m_mkdir.get(), &QtcProcess::done, this, [this] { - if (m_mkdir->result() != ProcessResult::FinishedWithSuccess) { - emit errorMessage(tr("Failed to create remote directories: %1") - .arg(QString::fromUtf8(m_mkdir->readAllStandardError()))); - setFinished(); - return; - } - deployFiles(); - m_mkdir.release()->deleteLater(); - }); - m_mkdir->start(); + m_mkdir.start(); } void RsyncDeployService::deployFiles() { - connect(&m_rsync, &QtcProcess::readyReadStandardOutput, this, [this] { - emit progressMessage(QString::fromLocal8Bit(m_rsync.readAllStandardOutput())); - }); - connect(&m_rsync, &QtcProcess::readyReadStandardError, this, [this] { - emit warningMessage(QString::fromLocal8Bit(m_rsync.readAllStandardError())); - }); - connect(&m_rsync, &QtcProcess::done, this, [this] { - auto notifyError = [this](const QString &message) { - emit errorMessage(message); - setFinished(); - }; - if (m_rsync.error() == QProcess::FailedToStart) - notifyError(tr("rsync failed to start: %1").arg(m_rsync.errorString())); - else if (m_rsync.exitStatus() == QProcess::CrashExit) - notifyError(tr("rsync crashed.")); - else if (m_rsync.exitCode() != 0) - notifyError(tr("rsync failed with exit code %1.").arg(m_rsync.exitCode())); - else - deployNextFile(); - }); - deployNextFile(); -} - -void RsyncDeployService::deployNextFile() -{ - if (m_deployableFiles.empty()) { - setFinished(); - return; - } - const DeployableFile file = m_deployableFiles.takeFirst(); - const RsyncCommandLine cmdLine = RsyncDeployStep::rsyncCommand(*connection(), m_flags); - QString localFilePath = file.localFilePath().toString(); - - // On Windows, rsync is either from msys or cygwin. Neither work with the other's ssh.exe. - if (HostOsInfo::isWindowsHost()) { - localFilePath = '/' + localFilePath.at(0) + localFilePath.mid(2); - if (anyOf(cmdLine.options, [](const QString &opt) { - return opt.contains("cygwin", Qt::CaseInsensitive); })) { - localFilePath.prepend("/cygdrive"); - } - } - - // TODO: consider adding "--progress" option for reporting the progress. - const QStringList args = QStringList(cmdLine.options) - << (localFilePath + (file.localFilePath().isDir() ? "/" : QString())) - << (cmdLine.remoteHostSpec + ':' + file.remoteFilePath()); - m_rsync.setCommand(CommandLine("rsync", args)); - m_rsync.start(); // TODO: Get rsync location from settings? + m_fileTransfer.setDevice(deviceConfiguration()); + m_fileTransfer.setTransferMethod(FileTransferMethod::Rsync); + m_fileTransfer.setRsyncFlags(m_flags); + m_fileTransfer.setFilesToTransfer(m_files); + m_fileTransfer.start(); } void RsyncDeployService::setFinished() { - if (m_mkdir) - m_mkdir.release()->deleteLater(); - m_rsync.close(); + m_mkdir.close(); + m_fileTransfer.stop(); handleDeploymentDone(); } } // namespace Internal +static const char s_defaultFlags[] = "-av"; + RsyncDeployStep::RsyncDeployStep(BuildStepList *bsl, Utils::Id id) : AbstractRemoteLinuxDeployStep(bsl, id) { @@ -188,7 +166,7 @@ RsyncDeployStep::RsyncDeployStep(BuildStepList *bsl, Utils::Id id) flags->setDisplayStyle(StringAspect::LineEditDisplay); flags->setSettingsKey("RemoteLinux.RsyncDeployStep.Flags"); flags->setLabelText(tr("Flags:")); - flags->setValue(defaultFlags()); + flags->setValue(s_defaultFlags); auto ignoreMissingFiles = addAspect(); ignoreMissingFiles->setSettingsKey("RemoteLinux.RsyncDeployStep.IgnoreMissingFiles");