diff options
8 files changed, 328 insertions, 155 deletions
diff --git a/src/plugins/remotelinux/abstractremotelinuxdeployservice.cpp b/src/plugins/remotelinux/abstractremotelinuxdeployservice.cpp index 4da0fff0fe..6e663bf817 100644 --- a/src/plugins/remotelinux/abstractremotelinuxdeployservice.cpp +++ b/src/plugins/remotelinux/abstractremotelinuxdeployservice.cpp @@ -96,14 +96,22 @@ SshConnection *AbstractRemoteLinuxDeployService::connection() const return d->connection; } -void AbstractRemoteLinuxDeployService::saveDeploymentTimeStamp(const DeployableFile &deployableFile) +void AbstractRemoteLinuxDeployService::saveDeploymentTimeStamp(const DeployableFile &deployableFile, + const QDateTime &remoteTimestamp) { - d->deployTimes.saveDeploymentTimeStamp(deployableFile, profile()); + d->deployTimes.saveDeploymentTimeStamp(deployableFile, profile(), remoteTimestamp); } -bool AbstractRemoteLinuxDeployService::hasChangedSinceLastDeployment(const DeployableFile &deployableFile) const +bool AbstractRemoteLinuxDeployService::hasLocalFileChanged( + const DeployableFile &deployableFile) const { - return d->deployTimes.hasChangedSinceLastDeployment(deployableFile, profile()); + return d->deployTimes.hasLocalFileChanged(deployableFile, profile()); +} + +bool AbstractRemoteLinuxDeployService::hasRemoteFileChanged( + const DeployableFile &deployableFile, const QDateTime &remoteTimestamp) const +{ + return d->deployTimes.hasRemoteFileChanged(deployableFile, profile(), remoteTimestamp); } void AbstractRemoteLinuxDeployService::setTarget(Target *target) diff --git a/src/plugins/remotelinux/abstractremotelinuxdeployservice.h b/src/plugins/remotelinux/abstractremotelinuxdeployservice.h index 0553472b95..9e55708d65 100644 --- a/src/plugins/remotelinux/abstractremotelinuxdeployservice.h +++ b/src/plugins/remotelinux/abstractremotelinuxdeployservice.h @@ -76,8 +76,12 @@ protected: ProjectExplorer::IDevice::ConstPtr deviceConfiguration() const; QSsh::SshConnection *connection() const; - void saveDeploymentTimeStamp(const ProjectExplorer::DeployableFile &deployableFile); - bool hasChangedSinceLastDeployment(const ProjectExplorer::DeployableFile &deployableFile) const; + void saveDeploymentTimeStamp(const ProjectExplorer::DeployableFile &deployableFile, + const QDateTime &remoteTimestamp); + + bool hasLocalFileChanged(const ProjectExplorer::DeployableFile &deployableFile) const; + bool hasRemoteFileChanged(const ProjectExplorer::DeployableFile &deployableFile, + const QDateTime &remoteTimestamp) const; void handleDeviceSetupDone(bool success); void handleDeploymentDone(); diff --git a/src/plugins/remotelinux/abstractuploadandinstallpackageservice.cpp b/src/plugins/remotelinux/abstractuploadandinstallpackageservice.cpp index 5974a6d557..16c1e6fc29 100644 --- a/src/plugins/remotelinux/abstractuploadandinstallpackageservice.cpp +++ b/src/plugins/remotelinux/abstractuploadandinstallpackageservice.cpp @@ -31,6 +31,7 @@ #include <projectexplorer/deployablefile.h> #include <utils/qtcassert.h> +#include <QDateTime> #include <QString> using namespace ProjectExplorer; @@ -87,7 +88,7 @@ QString AbstractUploadAndInstallPackageService::uploadDir() const bool AbstractUploadAndInstallPackageService::isDeploymentNecessary() const { - return hasChangedSinceLastDeployment(DeployableFile(packageFilePath(), QString())); + return hasLocalFileChanged(DeployableFile(packageFilePath(), QString())); } void AbstractUploadAndInstallPackageService::doDeviceSetup() @@ -164,7 +165,7 @@ void AbstractUploadAndInstallPackageService::handleInstallationFinished(const QS QTC_ASSERT(d->state == Installing, return); if (errorMsg.isEmpty()) { - saveDeploymentTimeStamp(DeployableFile(packageFilePath(), QString())); + saveDeploymentTimeStamp(DeployableFile(packageFilePath(), QString()), QDateTime()); emit progressMessage(tr("Package installed.")); } else { emit errorMessage(errorMsg); diff --git a/src/plugins/remotelinux/deploymenttimeinfo.cpp b/src/plugins/remotelinux/deploymenttimeinfo.cpp index fea219769b..31a4c22eef 100644 --- a/src/plugins/remotelinux/deploymenttimeinfo.cpp +++ b/src/plugins/remotelinux/deploymenttimeinfo.cpp @@ -46,6 +46,8 @@ const char LastDeployedSysrootsKey[] = "ProjectExplorer.RunConfiguration.LastDep const char LastDeployedFilesKey[] = "ProjectExplorer.RunConfiguration.LastDeployedFiles"; const char LastDeployedRemotePathsKey[] = "ProjectExplorer.RunConfiguration.LastDeployedRemotePaths"; const char LastDeployedTimesKey[] = "ProjectExplorer.RunConfiguration.LastDeployedTimes"; +const char LastDeployedLocalTimesKey[] = "RemoteLinux.LastDeployedLocalTimes"; +const char LastDeployedRemoteTimesKey[] = "RemoteLinux.LastDeployedRemoteTimes"; class DeployParameters { @@ -72,7 +74,29 @@ uint qHash(const DeployParameters &p) { class DeploymentTimeInfoPrivate { public: - QHash<DeployParameters, QDateTime> lastDeployed; + struct Timestamps + { + QDateTime local; + QDateTime remote; + }; + QHash<DeployParameters, Timestamps> lastDeployed; + + DeployParameters parameters(const ProjectExplorer::DeployableFile &deployableFile, + const ProjectExplorer::Kit *kit) const + { + QString systemRoot; + QString host; + + if (kit) { + if (SysRootKitInformation::hasSysRoot(kit)) + systemRoot = SysRootKitInformation::sysRoot(kit).toString(); + + const IDevice::ConstPtr deviceConfiguration = DeviceKitInformation::device(kit); + host = deviceConfiguration->sshParameters().host(); + } + + return DeployParameters(deployableFile, host, systemRoot); + } }; @@ -87,42 +111,27 @@ DeploymentTimeInfo::~DeploymentTimeInfo() } void DeploymentTimeInfo::saveDeploymentTimeStamp(const DeployableFile &deployableFile, - const Kit *kit) + const Kit *kit, const QDateTime &remoteTimestamp) { - if (!kit) - return; - - QString systemRoot; - if (SysRootKitInformation::hasSysRoot(kit)) - systemRoot = SysRootKitInformation::sysRoot(kit).toString(); - - const IDevice::ConstPtr deviceConfiguration = DeviceKitInformation::device(kit); - const QString host = deviceConfiguration->sshParameters().host(); - d->lastDeployed.insert( - DeployParameters(deployableFile, host, systemRoot), - QDateTime::currentDateTime()); + d->parameters(deployableFile, kit), + { deployableFile.localFilePath().toFileInfo().lastModified(), remoteTimestamp }); } -bool DeploymentTimeInfo::hasChangedSinceLastDeployment(const DeployableFile &deployableFile, - const ProjectExplorer::Kit *kit) const +bool DeploymentTimeInfo::hasLocalFileChanged(const DeployableFile &deployableFile, + const ProjectExplorer::Kit *kit) const { - if (!kit) - return false; - - QString systemRoot; - if (SysRootKitInformation::hasSysRoot(kit)) - systemRoot = SysRootKitInformation::sysRoot(kit).toString(); - - const IDevice::ConstPtr deviceConfiguration = DeviceKitInformation::device(kit); - const QString host = deviceConfiguration->sshParameters().host(); - - const DeployParameters dp(deployableFile, host, systemRoot); - - const QDateTime &lastDeployed = d->lastDeployed.value(dp); + const auto &lastDeployed = d->lastDeployed.value(d->parameters(deployableFile, kit)); const QDateTime lastModified = deployableFile.localFilePath().toFileInfo().lastModified(); + return !lastDeployed.local.isValid() || lastModified != lastDeployed.local; +} - return !lastDeployed.isValid() || (lastModified > lastDeployed); +bool DeploymentTimeInfo::hasRemoteFileChanged(const DeployableFile &deployableFile, + const ProjectExplorer::Kit *kit, + const QDateTime &remoteTimestamp) const +{ + const auto &lastDeployed = d->lastDeployed.value(d->parameters(deployableFile, kit)); + return !lastDeployed.remote.isValid() || remoteTimestamp != lastDeployed.remote; } QVariantMap DeploymentTimeInfo::exportDeployTimes() const @@ -132,21 +141,24 @@ QVariantMap DeploymentTimeInfo::exportDeployTimes() const QVariantList fileList; QVariantList sysrootList; QVariantList remotePathList; - QVariantList timeList; - typedef QHash<DeployParameters, QDateTime>::ConstIterator DepIt; + QVariantList localTimeList; + QVariantList remoteTimeList; + using DepIt = QHash<DeployParameters, DeploymentTimeInfoPrivate::Timestamps>::ConstIterator; for (DepIt it = d->lastDeployed.constBegin(); it != d->lastDeployed.constEnd(); ++it) { fileList << it.key().file.localFilePath().toString(); remotePathList << it.key().file.remoteDirectory(); hostList << it.key().host; sysrootList << it.key().sysroot; - timeList << it.value(); + localTimeList << it.value().local; + remoteTimeList << it.value().remote; } map.insert(QLatin1String(LastDeployedHostsKey), hostList); map.insert(QLatin1String(LastDeployedSysrootsKey), sysrootList); map.insert(QLatin1String(LastDeployedFilesKey), fileList); map.insert(QLatin1String(LastDeployedRemotePathsKey), remotePathList); - map.insert(QLatin1String(LastDeployedTimesKey), timeList); + map.insert(QLatin1String(LastDeployedLocalTimesKey), localTimeList); + map.insert(QLatin1String(LastDeployedRemoteTimesKey), remoteTimeList); return map; } @@ -157,16 +169,29 @@ void DeploymentTimeInfo::importDeployTimes(const QVariantMap &map) const QVariantList &fileList = map.value(QLatin1String(LastDeployedFilesKey)).toList(); const QVariantList &remotePathList = map.value(QLatin1String(LastDeployedRemotePathsKey)).toList(); - const QVariantList &timeList = map.value(QLatin1String(LastDeployedTimesKey)).toList(); + + QVariantList localTimesList; + const auto localTimes = map.find(QLatin1String(LastDeployedLocalTimesKey)); + if (localTimes != map.end()) { + localTimesList = localTimes.value().toList(); + } else { + localTimesList = map.value(QLatin1String(LastDeployedTimesKey)).toList(); + } + + const QVariantList remoteTimesList + = map.value(QLatin1String(LastDeployedRemoteTimesKey)).toList(); const int elemCount = qMin(qMin(qMin(hostList.size(), fileList.size()), - qMin(remotePathList.size(), timeList.size())), + qMin(remotePathList.size(), localTimesList.size())), sysrootList.size()); for (int i = 0; i < elemCount; ++i) { const DeployableFile df(fileList.at(i).toString(), remotePathList.at(i).toString()); const DeployParameters dp(df, hostList.at(i).toString(), sysrootList.at(i).toString()); - d->lastDeployed.insert(dp, timeList.at(i).toDateTime()); + d->lastDeployed.insert(dp, { localTimesList.at(i).toDateTime(), + remoteTimesList.length() > i + ? remoteTimesList.at(i).toDateTime() + : QDateTime() }); } } diff --git a/src/plugins/remotelinux/deploymenttimeinfo.h b/src/plugins/remotelinux/deploymenttimeinfo.h index b0d14f829d..3c91be74c7 100644 --- a/src/plugins/remotelinux/deploymenttimeinfo.h +++ b/src/plugins/remotelinux/deploymenttimeinfo.h @@ -47,10 +47,15 @@ public: QVariantMap exportDeployTimes() const; void saveDeploymentTimeStamp(const ProjectExplorer::DeployableFile &deployableFile, - const ProjectExplorer::Kit *kit); + const ProjectExplorer::Kit *kit, + const QDateTime &remoteTimestamp); - bool hasChangedSinceLastDeployment(const ProjectExplorer::DeployableFile &deployableFile, - const ProjectExplorer::Kit *kit) const; + bool hasLocalFileChanged(const ProjectExplorer::DeployableFile &deployableFile, + const ProjectExplorer::Kit *kit) const; + + bool hasRemoteFileChanged(const ProjectExplorer::DeployableFile &deployableFile, + const ProjectExplorer::Kit *kit, + const QDateTime &remoteTimestamp) const; private: DeploymentTimeInfoPrivate *d; diff --git a/src/plugins/remotelinux/genericdirectuploadservice.cpp b/src/plugins/remotelinux/genericdirectuploadservice.cpp index e9cfaf5dc7..8ea7dbac2a 100644 --- a/src/plugins/remotelinux/genericdirectuploadservice.cpp +++ b/src/plugins/remotelinux/genericdirectuploadservice.cpp @@ -36,6 +36,8 @@ #include <QFileInfo> #include <QList> #include <QString> +#include <QDateTime> +#include <QHash> using namespace ProjectExplorer; using namespace QSsh; @@ -46,6 +48,23 @@ namespace { enum State { Inactive, InitializingSftp, Uploading }; } // anonymous namespace +enum class JobType { + PreQuery, + Upload, + Mkdir, + Ln, + Chmod, + PostQuery, + None +}; + +struct Job +{ + DeployableFile file; + JobType type = JobType::None; + QDateTime result; +}; + class GenericDirectUploadServicePrivate { public: @@ -54,12 +73,17 @@ public: bool incremental; bool ignoreMissingFiles; + bool uploadJobRunning = false; State state; + QList<DeployableFile> filesToUpload; + + QHash<SftpJobId, Job> runningJobs; + + SshRemoteProcess::Ptr runningProc; + DeployableFile runningProcFile; + SftpChannel::Ptr uploader; - SshRemoteProcess::Ptr mkdirProc; - SshRemoteProcess::Ptr lnProc; - SshRemoteProcess::Ptr chmodProc; QList<DeployableFile> deployableFiles; }; @@ -94,10 +118,14 @@ void GenericDirectUploadService::setIgnoreMissingFiles(bool ignoreMissingFiles) bool GenericDirectUploadService::isDeploymentNecessary() const { - d->filesToUpload.clear(); + QTC_ASSERT(d->filesToUpload.isEmpty(), d->filesToUpload.clear()); + QList<DeployableFile> collected; for (int i = 0; i < d->deployableFiles.count(); ++i) - checkDeploymentNeeded(d->deployableFiles.at(i)); - return !d->filesToUpload.isEmpty(); + collected.append(collectFilesToUpload(d->deployableFiles.at(i))); + + QTC_CHECK(collected.size() >= d->deployableFiles.size()); + d->deployableFiles = collected; + return !d->deployableFiles.isEmpty(); } void GenericDirectUploadService::doDeviceSetup() @@ -130,12 +158,13 @@ void GenericDirectUploadService::doDeploy() void GenericDirectUploadService::handleSftpInitialized() { QTC_ASSERT(d->state == InitializingSftp, setFinished(); return); - - Q_ASSERT(!d->filesToUpload.isEmpty()); + QTC_ASSERT(!d->deployableFiles.isEmpty(), setFinished(); return); connect(d->uploader.data(), &SftpChannel::finished, - this, &GenericDirectUploadService::handleUploadFinished); + this, &GenericDirectUploadService::handleJobFinished); + connect(d->uploader.data(), &SftpChannel::fileInfoAvailable, + this, &GenericDirectUploadService::handleFileInfoAvailable); d->state = Uploading; - uploadNextFile(); + queryFiles(); } void GenericDirectUploadService::handleSftpChannelError(const QString &message) @@ -147,94 +176,154 @@ void GenericDirectUploadService::handleSftpChannelError(const QString &message) handleDeploymentDone(); } -void GenericDirectUploadService::handleUploadFinished(SftpJobId jobId, const QString &errorMsg) +void GenericDirectUploadService::handleFileInfoAvailable(SftpJobId jobId, + const QList<SftpFileInfo> &fileInfos) { - Q_UNUSED(jobId); - - QTC_ASSERT(d->state == Uploading, setFinished(); return); + QTC_ASSERT(d->state == Uploading, return); + QTC_ASSERT(fileInfos.length() == 1, return); + auto it = d->runningJobs.find(jobId); + QTC_ASSERT(it != d->runningJobs.end(), return); + it->result = QDateTime::fromTime_t(fileInfos.at(0).mtime); +} - const DeployableFile df = d->filesToUpload.takeFirst(); - if (!errorMsg.isEmpty()) { - QString errorString = tr("Upload of file \"%1\" failed. The server said: \"%2\".") - .arg(df.localFilePath().toUserOutput(), errorMsg); - if (errorMsg == QLatin1String("Failure") - && df.remoteDirectory().contains(QLatin1String("/bin"))) { - errorString += QLatin1Char(' ') + tr("If \"%1\" is currently running " - "on the remote host, you might need to stop it first.").arg(df.remoteFilePath()); +void GenericDirectUploadService::handleJobFinished(SftpJobId jobId, const QString &errorMsg) +{ + auto it = d->runningJobs.find(jobId); + QTC_ASSERT(it != d->runningJobs.end(), return); + + Job job = *it; + d->runningJobs.erase(it); + + switch (job.type) { + case JobType::PreQuery: + if (hasRemoteFileChanged(job.file, job.result)) { + d->filesToUpload.append(job.file); + if (!d->uploadJobRunning) + uploadNextFile(); + } else { + tryFinish(); + } + break; + case JobType::Upload: + QTC_CHECK(d->uploadJobRunning); + + if (!errorMsg.isEmpty()) { + QString errorString = tr("Upload of file \"%1\" failed. The server said: \"%2\".") + .arg(job.file.localFilePath().toUserOutput(), errorMsg); + if (errorMsg == QLatin1String("Failure") + && job.file.remoteDirectory().contains(QLatin1String("/bin"))) { + errorString += QLatin1Char(' ') + + tr("If \"%1\" is currently running on the remote host, " + "you might need to stop it first.").arg(job.file.remoteFilePath()); + } + emit errorMessage(errorString); + setFinished(); + handleDeploymentDone(); } - emit errorMessage(errorString); - setFinished(); - handleDeploymentDone(); - } else { - saveDeploymentTimeStamp(df); // This is done for Windows. - if (df.isExecutable()) { + if (job.file.isExecutable()) { const QString command = QLatin1String("chmod a+x ") - + Utils::QtcProcess::quoteArgUnix(df.remoteFilePath()); - d->chmodProc = connection()->createRemoteProcess(command.toUtf8()); - connect(d->chmodProc.data(), &SshRemoteProcess::closed, - this, &GenericDirectUploadService::handleChmodFinished); - connect(d->chmodProc.data(), &SshRemoteProcess::readyReadStandardOutput, + + Utils::QtcProcess::quoteArgUnix(job.file.remoteFilePath()); + d->runningProc = connection()->createRemoteProcess(command.toUtf8()); + d->runningProcFile = job.file; + connect(d->runningProc.data(), &SshRemoteProcess::closed, + this, &GenericDirectUploadService::handleUploadProcFinished); + connect(d->runningProc.data(), &SshRemoteProcess::readyReadStandardOutput, this, &GenericDirectUploadService::handleStdOutData); - connect(d->chmodProc.data(), &SshRemoteProcess::readyReadStandardError, + connect(d->runningProc.data(), &SshRemoteProcess::readyReadStandardError, this, &GenericDirectUploadService::handleStdErrData); - connect(d->chmodProc.data(), &SshRemoteProcess::readChannelFinished, + connect(d->runningProc.data(), &SshRemoteProcess::readChannelFinished, this, &GenericDirectUploadService::handleReadChannelFinished); - d->chmodProc->start(); + d->runningProc->start(); } else { + d->uploadJobRunning = false; + const SftpJobId jobId = d->uploader->statFile(job.file.remoteFilePath()); + if (jobId == SftpInvalidJob) { + emit errorMessage(tr("SFTP stat query for %1 failed.") + .arg(job.file.remoteFilePath())); + saveDeploymentTimeStamp(job.file, QDateTime()); + } else { + d->runningJobs.insert(jobId, { job.file, JobType::PostQuery, QDateTime() }); + } uploadNextFile(); } + break; + case JobType::PostQuery: + if (!errorMsg.isEmpty()) { + emit warningMessage(tr("Could not determine remote timestamp of %1: %2") + .arg(job.file.remoteFilePath()).arg(errorMsg)); + } + saveDeploymentTimeStamp(job.file, job.result); + tryFinish(); + break; + default: + QTC_CHECK(false); + break; } } -void GenericDirectUploadService::handleLnFinished(int exitStatus) +void GenericDirectUploadService::clearRunningProc() +{ + d->runningProc.clear(); + d->runningProcFile = DeployableFile(); + d->uploadJobRunning = false; +} + +void GenericDirectUploadService::handleUploadProcFinished(int exitStatus) { QTC_ASSERT(d->state == Uploading, setFinished(); return); + QTC_ASSERT(d->uploadJobRunning, return); - const DeployableFile df = d->filesToUpload.takeFirst(); - const QString nativePath = df.localFilePath().toUserOutput(); - if (exitStatus != SshRemoteProcess::NormalExit || d->lnProc->exitCode() != 0) { - emit errorMessage(tr("Failed to upload file \"%1\".").arg(nativePath)); - setFinished(); - handleDeploymentDone(); - return; + if (exitStatus != SshRemoteProcess::NormalExit || d->runningProc->exitCode() != 0) + handleProcFailure(); + else + runPostQueryOnProcResult(); +} + +void GenericDirectUploadService::handleProcFailure() +{ + emit errorMessage(tr("Failed to upload file \"%1\".") + .arg(d->runningProcFile.localFilePath().toUserOutput())); + clearRunningProc(); + setFinished(); + handleDeploymentDone(); +} + +void GenericDirectUploadService::runPostQueryOnProcResult() +{ + const SftpJobId jobId = d->uploader->statFile(d->runningProcFile.remoteFilePath()); + if (jobId == SftpInvalidJob) { + emit errorMessage(tr("SFTP stat query for %1 failed.") + .arg(d->runningProcFile.remoteFilePath())); + saveDeploymentTimeStamp(d->runningProcFile, QDateTime()); } else { - saveDeploymentTimeStamp(df); - uploadNextFile(); + d->runningJobs.insert(jobId, { d->runningProcFile, JobType::PostQuery, QDateTime() }); } + clearRunningProc(); + uploadNextFile(); } -void GenericDirectUploadService::handleChmodFinished(int exitStatus) +void GenericDirectUploadService::tryFinish() { - QTC_ASSERT(d->state == Uploading, setFinished(); return); - - if (exitStatus != SshRemoteProcess::NormalExit || d->chmodProc->exitCode() != 0) { - emit errorMessage(tr("Failed to set executable flag.")); + if (d->filesToUpload.isEmpty() && d->runningJobs.isEmpty() && d->runningProc.isNull()) { + emit progressMessage(tr("All files successfully deployed.")); setFinished(); handleDeploymentDone(); - return; } - uploadNextFile(); } void GenericDirectUploadService::handleMkdirFinished(int exitStatus) { QTC_ASSERT(d->state == Uploading, setFinished(); return); - const DeployableFile &df = d->filesToUpload.first(); - QFileInfo fi = df.localFilePath().toFileInfo(); - const QString nativePath = df.localFilePath().toUserOutput(); - if (exitStatus != SshRemoteProcess::NormalExit || d->mkdirProc->exitCode() != 0) { - emit errorMessage(tr("Failed to upload file \"%1\".").arg(nativePath)); - setFinished(); - handleDeploymentDone(); + QFileInfo fi = d->runningProcFile.localFilePath().toFileInfo(); + if (exitStatus != SshRemoteProcess::NormalExit || d->runningProc->exitCode() != 0) { + handleProcFailure(); } else if (fi.isDir()) { - saveDeploymentTimeStamp(df); - d->filesToUpload.removeFirst(); - uploadNextFile(); + runPostQueryOnProcResult(); } else { - const QString remoteFilePath = df.remoteDirectory() + QLatin1Char('/') + fi.fileName(); + const QString remoteFilePath = d->runningProcFile.remoteFilePath(); if (fi.isSymLink()) { const QString target = fi.dir().relativeFilePath(fi.symLinkTarget()); // see QTBUG-5817. const QStringList args = QStringList() << QLatin1String("ln") << QLatin1String("-sf") @@ -242,31 +331,37 @@ void GenericDirectUploadService::handleMkdirFinished(int exitStatus) const QString command = Utils::QtcProcess::joinArgs(args, Utils::OsTypeLinux); // See comment in SftpChannel::createLink as to why we can't use it. - d->lnProc = connection()->createRemoteProcess(command.toUtf8()); - connect(d->lnProc.data(), &SshRemoteProcess::closed, - this, &GenericDirectUploadService::handleLnFinished); - connect(d->lnProc.data(), &SshRemoteProcess::readyReadStandardOutput, + d->runningProc = connection()->createRemoteProcess(command.toUtf8()); + connect(d->runningProc.data(), &SshRemoteProcess::closed, + this, &GenericDirectUploadService::handleUploadProcFinished); + connect(d->runningProc.data(), &SshRemoteProcess::readyReadStandardOutput, this, &GenericDirectUploadService::handleStdOutData); - connect(d->lnProc.data(), &SshRemoteProcess::readyReadStandardError, + connect(d->runningProc.data(), &SshRemoteProcess::readyReadStandardError, this, &GenericDirectUploadService::handleStdErrData); - connect(d->lnProc.data(), &SshRemoteProcess::readChannelFinished, + connect(d->runningProc.data(), &SshRemoteProcess::readChannelFinished, this, &GenericDirectUploadService::handleReadChannelFinished); - d->lnProc->start(); + d->runningProc->start(); } else { - const SftpJobId job = d->uploader->uploadFile(df.localFilePath().toString(), - remoteFilePath, SftpOverwriteExisting); + const SftpJobId job = d->uploader->uploadFile( + d->runningProcFile.localFilePath().toString(), remoteFilePath, + SftpOverwriteExisting); if (job == SftpInvalidJob) { const QString message = tr("Failed to upload file \"%1\": " - "Could not open for reading.").arg(nativePath); + "Could not open for reading.") + .arg(d->runningProcFile.localFilePath().toUserOutput()); + clearRunningProc(); if (d->ignoreMissingFiles) { emit warningMessage(message); - d->filesToUpload.removeFirst(); uploadNextFile(); } else { emit errorMessage(message); setFinished(); handleDeploymentDone(); } + } else { + d->runningJobs[job] = { d->runningProcFile, JobType::Upload, QDateTime() }; + clearRunningProc(); + d->uploadJobRunning = true; } } } @@ -301,54 +396,55 @@ void GenericDirectUploadService::stopDeployment() handleDeploymentDone(); } -void GenericDirectUploadService::checkDeploymentNeeded(const DeployableFile &deployable) const +QList<DeployableFile> GenericDirectUploadService::collectFilesToUpload( + const DeployableFile &deployable) const { + QList<DeployableFile> collected({deployable}); QFileInfo fileInfo = deployable.localFilePath().toFileInfo(); if (fileInfo.isDir()) { const QStringList files = QDir(deployable.localFilePath().toString()) .entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot); - if (files.isEmpty() && (!d->incremental || hasChangedSinceLastDeployment(deployable))) - d->filesToUpload << deployable; - foreach (const QString &fileName, files) { + for (const QString &fileName : files) { const QString localFilePath = deployable.localFilePath().toString() + QLatin1Char('/') + fileName; const QString remoteDir = deployable.remoteDirectory() + QLatin1Char('/') + fileInfo.fileName(); - checkDeploymentNeeded(DeployableFile(localFilePath, remoteDir)); + collected.append(collectFilesToUpload(DeployableFile(localFilePath, remoteDir))); } - } else if (!d->incremental || hasChangedSinceLastDeployment(deployable)) { - d->filesToUpload << deployable; } + return collected; } void GenericDirectUploadService::setFinished() { d->state = Inactive; - if (d->mkdirProc) - disconnect(d->mkdirProc.data(), 0, this, 0); - if (d->lnProc) - disconnect(d->lnProc.data(), 0, this, 0); + if (d->runningProc) + disconnect(d->runningProc.data(), nullptr, this, nullptr); if (d->uploader) { - disconnect(d->uploader.data(), 0, this, 0); + disconnect(d->uploader.data(), nullptr, this, nullptr); d->uploader->closeChannel(); } + clearRunningProc(); + d->uploadJobRunning = false; + d->runningJobs.clear(); + d->filesToUpload.clear(); } void GenericDirectUploadService::uploadNextFile() { + QTC_ASSERT(!d->uploadJobRunning, return); + if (d->filesToUpload.isEmpty()) { - emit progressMessage(tr("All files successfully deployed.")); - setFinished(); - handleDeploymentDone(); + tryFinish(); return; } - const DeployableFile &df = d->filesToUpload.first(); + const DeployableFile df = d->filesToUpload.takeFirst(); + QString dirToCreate = df.remoteDirectory(); if (dirToCreate.isEmpty()) { - emit warningMessage(tr("Warning: No remote path set for local file \"%1\". Skipping upload.") - .arg(df.localFilePath().toUserOutput())); - d->filesToUpload.takeFirst(); + emit warningMessage(tr("Warning: No remote path set for local file \"%1\". " + "Skipping upload.").arg(df.localFilePath().toUserOutput())); uploadNextFile(); return; } @@ -358,18 +454,44 @@ void GenericDirectUploadService::uploadNextFile() dirToCreate += QLatin1Char('/') + fi.fileName(); const QString command = QLatin1String("mkdir -p ") + Utils::QtcProcess::quoteArgUnix(dirToCreate); - d->mkdirProc = connection()->createRemoteProcess(command.toUtf8()); - connect(d->mkdirProc.data(), &SshRemoteProcess::closed, + QTC_CHECK(d->runningProc.isNull()); + d->runningProc = connection()->createRemoteProcess(command.toUtf8()); + connect(d->runningProc.data(), &SshRemoteProcess::closed, this, &GenericDirectUploadService::handleMkdirFinished); - connect(d->mkdirProc.data(), &SshRemoteProcess::readyReadStandardOutput, + connect(d->runningProc.data(), &SshRemoteProcess::readyReadStandardOutput, this, &GenericDirectUploadService::handleStdOutData); - connect(d->mkdirProc.data(), &SshRemoteProcess::readyReadStandardError, + connect(d->runningProc.data(), &SshRemoteProcess::readyReadStandardError, this, &GenericDirectUploadService::handleStdErrData); - connect(d->mkdirProc.data(), &SshRemoteProcess::readChannelFinished, + connect(d->runningProc.data(), &SshRemoteProcess::readChannelFinished, this, &GenericDirectUploadService::handleReadChannelFinished); emit progressMessage(tr("Uploading file \"%1\"...") - .arg(df.localFilePath().toUserOutput())); - d->mkdirProc->start(); + .arg(df.localFilePath().toUserOutput())); + d->runningProcFile = df; + d->runningProc->start(); + d->uploadJobRunning = true; +} + +void GenericDirectUploadService::queryFiles() +{ + QTC_ASSERT(d->state == Uploading, return); + + for (const DeployableFile &file : d->deployableFiles) { + if (!d->incremental || hasLocalFileChanged(file)) { + d->filesToUpload.append(file); + continue; + } + + const SftpJobId jobId = d->uploader->statFile(file.remoteFilePath()); + if (jobId == SftpInvalidJob) { + emit warningMessage(tr("SFTP stat query for %1 failed.").arg(file.remoteFilePath())); + d->filesToUpload.append(file); + continue; + } + + d->runningJobs.insert(jobId, { file, JobType::PreQuery, QDateTime() }); + } + + uploadNextFile(); } } //namespace RemoteLinux diff --git a/src/plugins/remotelinux/genericdirectuploadservice.h b/src/plugins/remotelinux/genericdirectuploadservice.h index 6659619140..4f5dbb71b2 100644 --- a/src/plugins/remotelinux/genericdirectuploadservice.h +++ b/src/plugins/remotelinux/genericdirectuploadservice.h @@ -62,17 +62,25 @@ public: private: void handleSftpInitialized(); void handleSftpChannelError(const QString &errorMessage); - void handleUploadFinished(QSsh::SftpJobId jobId, const QString &errorMsg); + void handleFileInfoAvailable(QSsh::SftpJobId jobId, const QList<QSsh::SftpFileInfo> &fileInfos); + void handleJobFinished(QSsh::SftpJobId jobId, const QString &errorMsg); + void handleUploadProcFinished(int exitStatus); void handleMkdirFinished(int exitStatus); - void handleLnFinished(int exitStatus); - void handleChmodFinished(int exitStatus); void handleStdOutData(); void handleStdErrData(); void handleReadChannelFinished(); - void checkDeploymentNeeded(const ProjectExplorer::DeployableFile &file) const; + QList<ProjectExplorer::DeployableFile> collectFilesToUpload( + const ProjectExplorer::DeployableFile &file) const; void setFinished(); void uploadNextFile(); + void queryFiles(); + void clearRunningProc(); + + void handleProcFailure(); + void runPostQueryOnProcResult(); + + void tryFinish(); Internal::GenericDirectUploadServicePrivate * const d; }; diff --git a/src/plugins/remotelinux/tarpackagecreationstep.cpp b/src/plugins/remotelinux/tarpackagecreationstep.cpp index 2a2955d3f5..010b127a63 100644 --- a/src/plugins/remotelinux/tarpackagecreationstep.cpp +++ b/src/plugins/remotelinux/tarpackagecreationstep.cpp @@ -197,7 +197,7 @@ void TarPackageCreationStep::addNeededDeploymentFiles( { const QFileInfo fileInfo = deployable.localFilePath().toFileInfo(); if (!fileInfo.isDir()) { - if (m_deployTimes.hasChangedSinceLastDeployment(deployable, kit)) + if (m_deployTimes.hasLocalFileChanged(deployable, kit)) m_files << deployable; return; } @@ -400,7 +400,7 @@ void TarPackageCreationStep::deployFinished(bool success) // Store files that have been tar'd and successfully deployed const auto files = m_files; for (const DeployableFile &file : files) - m_deployTimes.saveDeploymentTimeStamp(file, kit); + m_deployTimes.saveDeploymentTimeStamp(file, kit, QDateTime()); } QString TarPackageCreationStep::packageFileName() const |