aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2018-10-11 11:08:50 +0200
committerUlf Hermann <ulf.hermann@qt.io>2018-10-17 13:13:32 +0000
commitf4a12d488c17c4fe865325f91084cbc1c02d5a34 (patch)
tree5972e942dd0cf79b7121255753a886652dcfb9cd
parentd6c605d84c1eb20e3614532aa0d535d8506da8dd (diff)
RemoteLinux: Check remote files when deploying
We discern between local and remote timestamps, and if the local timestamp matches, we still query the remote file via SSH to see if that one differs. If so, we still upload the file. After uploading a file we always query its remote timestamp and save it along with the local one. Fixes: QTCREATORBUG-21225 Change-Id: Ieeae1c3e61907beb7ad0fe9b03772af6ae351be7 Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
-rw-r--r--src/plugins/remotelinux/abstractremotelinuxdeployservice.cpp16
-rw-r--r--src/plugins/remotelinux/abstractremotelinuxdeployservice.h8
-rw-r--r--src/plugins/remotelinux/abstractuploadandinstallpackageservice.cpp5
-rw-r--r--src/plugins/remotelinux/deploymenttimeinfo.cpp99
-rw-r--r--src/plugins/remotelinux/deploymenttimeinfo.h11
-rw-r--r--src/plugins/remotelinux/genericdirectuploadservice.cpp324
-rw-r--r--src/plugins/remotelinux/genericdirectuploadservice.h16
-rw-r--r--src/plugins/remotelinux/tarpackagecreationstep.cpp4
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