// Copyright (C) 2018 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 #include "rsyncdeploystep.h" #include "abstractremotelinuxdeploystep.h" #include "abstractremotelinuxdeployservice.h" #include "abstractremotelinuxdeploystep.h" #include "remotelinux_constants.h" #include "remotelinuxtr.h" #include #include #include #include #include #include #include #include #include using namespace ProjectExplorer; using namespace Utils; namespace RemoteLinux::Internal { class RsyncDeployService : public AbstractRemoteLinuxDeployService { public: RsyncDeployService() { connect(&m_mkdir, &QtcProcess::done, this, [this] { if (m_mkdir.result() != ProcessResult::FinishedWithSuccess) { QString finalMessage = m_mkdir.errorString(); const QString stdErr = m_mkdir.cleanedStdErr(); if (!stdErr.isEmpty()) { if (!finalMessage.isEmpty()) finalMessage += '\n'; finalMessage += stdErr; } emit errorMessage(Tr::tr("Deploy via rsync: failed to create remote directories:") + '\n' + finalMessage); setFinished(); return; } deployFiles(); }); connect(&m_mkdir, &QtcProcess::readyReadStandardError, this, [this] { emit stdErrData(QString::fromLocal8Bit(m_mkdir.readAllStandardError())); }); 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::tr("rsync failed to start: %1").arg(result.m_errorString)); else if (result.m_exitStatus == QProcess::CrashExit) notifyError(Tr::tr("rsync crashed.")); else if (result.m_exitCode != 0) notifyError(Tr::tr("rsync failed with exit code %1.").arg(result.m_exitCode)); else setFinished(); }); } void setDeployableFiles(const QList &files); void setIgnoreMissingFiles(bool ignore) { m_ignoreMissingFiles = ignore; } void setFlags(const QString &flags) { m_flags = flags; } private: bool isDeploymentNecessary() const override; void doDeploy() override; void stopDeployment() override { setFinished(); }; void createRemoteDirectories(); void deployFiles(); void setFinished(); mutable FilesToTransfer m_files; bool m_ignoreMissingFiles = false; QString m_flags; 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 { if (m_ignoreMissingFiles) Utils::erase(m_files, [](const FileToTransfer &file) { return !file.m_source.exists(); }); return !m_files.empty(); } void RsyncDeployService::doDeploy() { createRemoteDirectories(); } void RsyncDeployService::createRemoteDirectories() { QStringList remoteDirs; for (const FileToTransfer &file : std::as_const(m_files)) remoteDirs << file.m_target.parentDir().path(); remoteDirs.sort(); remoteDirs.removeDuplicates(); m_mkdir.setCommand({deviceConfiguration()->filePath("mkdir"), QStringList("-p") + remoteDirs}); m_mkdir.start(); } void RsyncDeployService::deployFiles() { m_fileTransfer.setTransferMethod(FileTransferMethod::Rsync); m_fileTransfer.setRsyncFlags(m_flags); m_fileTransfer.setFilesToTransfer(m_files); m_fileTransfer.start(); } void RsyncDeployService::setFinished() { m_mkdir.close(); m_fileTransfer.stop(); handleDeploymentDone(); } // RsyncDeployStep class RsyncDeployStep : public AbstractRemoteLinuxDeployStep { public: RsyncDeployStep(BuildStepList *bsl, Id id) : AbstractRemoteLinuxDeployStep(bsl, id) { auto service = createDeployService(); auto flags = addAspect(); flags->setDisplayStyle(StringAspect::LineEditDisplay); flags->setSettingsKey("RemoteLinux.RsyncDeployStep.Flags"); flags->setLabelText(Tr::tr("Flags:")); flags->setValue(FileTransferSetupData::defaultRsyncFlags()); auto ignoreMissingFiles = addAspect(); ignoreMissingFiles->setSettingsKey("RemoteLinux.RsyncDeployStep.IgnoreMissingFiles"); ignoreMissingFiles->setLabel(Tr::tr("Ignore missing files:"), BoolAspect::LabelPlacement::InExtraLabel); ignoreMissingFiles->setValue(false); setInternalInitializer([service, flags, ignoreMissingFiles] { service->setIgnoreMissingFiles(ignoreMissingFiles->value()); service->setFlags(flags->value()); return service->isDeploymentPossible(); }); setRunPreparer([this, service] { service->setDeployableFiles(target()->deploymentData().allFiles()); }); } }; // RsyncDeployStepFactory RsyncDeployStepFactory::RsyncDeployStepFactory() { registerStep(Constants::RsyncDeployStepId); setDisplayName(Tr::tr("Deploy files via rsync")); setSupportedConfiguration(RemoteLinux::Constants::DeployToGenericLinux); setSupportedStepList(ProjectExplorer::Constants::BUILDSTEPS_DEPLOY); } } // RemoteLinux::Internal