From 818f5f0e78baf85d60dedf3fca31221c9329aafd Mon Sep 17 00:00:00 2001
From: Friedemann Kleint <Friedemann.Kleint@nokia.com>
Date: Mon, 22 Nov 2010 14:53:20 +0100
Subject: [PATCH] VCS: Allow to run commands synchronously

Run some commands synchronously. This avoids issues with the UI
reacting to file changes done by the VCS on windows which can lead
to crashes.

Task-number: QTCREATORBUG-3021
Reviewed-by: Tobias Hunger
---
 src/plugins/perforce/perforceplugin.cpp     |   4 +-
 src/plugins/subversion/subversionplugin.cpp |   4 +-
 src/plugins/vcsbase/vcsbaseplugin.cpp       | 143 +++++++++++++++-----
 src/plugins/vcsbase/vcsbaseplugin.h         |   4 +-
 4 files changed, 116 insertions(+), 39 deletions(-)

diff --git a/src/plugins/perforce/perforceplugin.cpp b/src/plugins/perforce/perforceplugin.cpp
index e7407821d22..9325dc88b6e 100644
--- a/src/plugins/perforce/perforceplugin.cpp
+++ b/src/plugins/perforce/perforceplugin.cpp
@@ -934,13 +934,13 @@ bool PerforcePlugin::vcsMove(const QString &workingDir, const QString &from, con
     QStringList args;
     args << QLatin1String("edit") << from;
     const PerforceResponse editResult = runP4Cmd(workingDir, args,
-                                                 CommandToWindow|StdOutToWindow|StdErrToWindow|ErrorToWindow);
+                                                 RunFullySynchronous|CommandToWindow|StdOutToWindow|StdErrToWindow|ErrorToWindow);
     if (editResult.error)
         return false;
     args.clear();
     args << QLatin1String("move") << from << to;
     const PerforceResponse moveResult = runP4Cmd(workingDir, args,
-                                                 CommandToWindow|StdOutToWindow|StdErrToWindow|ErrorToWindow);
+                                                 RunFullySynchronous|CommandToWindow|StdOutToWindow|StdErrToWindow|ErrorToWindow);
     return !moveResult.error;
 }
 
diff --git a/src/plugins/subversion/subversionplugin.cpp b/src/plugins/subversion/subversionplugin.cpp
index c3a1f9f5bc9..f556bba3081 100644
--- a/src/plugins/subversion/subversionplugin.cpp
+++ b/src/plugins/subversion/subversionplugin.cpp
@@ -1219,11 +1219,9 @@ bool SubversionPlugin::vcsMove(const QString &workingDir, const QString &from, c
 {
     QStringList args(QLatin1String("move"));
     args << QDir::toNativeSeparators(from) << QDir::toNativeSeparators(to);
-    qDebug()<<args;
     const SubversionResponse response =
             runSvn(workingDir, args, m_settings.timeOutMS(),
-                   SshPasswordPrompt|ShowStdOutInLogWindow);
-    qDebug() << response.stdOut << "\n"<<response.stdErr;
+                   SshPasswordPrompt|ShowStdOutInLogWindow|FullySynchronously);
     return !response.error;
 }
 
diff --git a/src/plugins/vcsbase/vcsbaseplugin.cpp b/src/plugins/vcsbase/vcsbaseplugin.cpp
index 14b395f5873..9d7b2b5310d 100644
--- a/src/plugins/vcsbase/vcsbaseplugin.cpp
+++ b/src/plugins/vcsbase/vcsbaseplugin.cpp
@@ -50,6 +50,7 @@
 #include <QtCore/QDir>
 #include <QtCore/QSharedData>
 #include <QtCore/QScopedPointer>
+#include <QtCore/QSharedPointer>
 #include <QtCore/QProcessEnvironment>
 #include <QtCore/QTextStream>
 #include <QtCore/QTextCodec>
@@ -701,6 +702,72 @@ void VCSBasePlugin::setProcessEnvironment(QProcessEnvironment *e, bool forceCLoc
         e->insert(QLatin1String("SSH_ASKPASS"), sshPromptBinary);
 }
 
+// Run a process fully synchronously, returning Utils::SynchronousProcessResponse
+// response struct and using the VCSBasePlugin flags as applicable
+static Utils::SynchronousProcessResponse
+    runVCS_FullySynchronously(const QString &workingDir,
+                              const QString &binary,
+                              const QStringList &arguments,
+                              int timeOutMS,
+                              QProcessEnvironment env,
+                              unsigned flags,
+                              QTextCodec *outputCodec = 0)
+{
+    VCSBase::VCSBaseOutputWindow *outputWindow = VCSBase::VCSBaseOutputWindow::instance();
+
+    // Set up process
+    unsigned processFlags = 0;
+    if (VCSBasePlugin::isSshPromptConfigured() && (flags & VCSBasePlugin::SshPasswordPrompt))
+        processFlags |= Utils::SynchronousProcess::UnixTerminalDisabled;
+    QSharedPointer<QProcess> process = Utils::SynchronousProcess::createProcess(processFlags);
+    if (!workingDir.isEmpty())
+        process->setWorkingDirectory(workingDir);
+    process->setProcessEnvironment(env);
+    if (flags & VCSBasePlugin::MergeOutputChannels)
+        process->setProcessChannelMode(QProcess::MergedChannels);
+
+    // Start
+    process->start(binary, arguments);
+    Utils::SynchronousProcessResponse response;
+    if (!process->waitForStarted()) {
+        response.result = Utils::SynchronousProcessResponse::StartFailed;
+        return response;
+    }
+
+    // process output
+    QByteArray stdOut;
+    QByteArray stdErr;
+    const bool timedOut =
+            !Utils::SynchronousProcess::readDataFromProcess(*process.data(), timeOutMS,
+                                                            &stdOut, &stdErr, true);
+
+    if (!stdErr.isEmpty()) {
+        response.stdErr = QString::fromLocal8Bit(stdErr).remove('\r');
+        if (!(flags & VCSBasePlugin::SuppressStdErrInLogWindow))
+            outputWindow->append(response.stdErr);
+    }
+
+    if (!stdOut.isEmpty()) {
+        response.stdOut = (outputCodec ? outputCodec->toUnicode(stdOut) : QString::fromLocal8Bit(stdOut))
+                          .remove('\r');
+        if (flags & VCSBasePlugin::ShowStdOutInLogWindow)
+            outputWindow->append(response.stdOut);
+    }
+
+    // Result
+    if (timedOut) {
+        response.result = Utils::SynchronousProcessResponse::Hang;
+    } else if (process->exitStatus() != QProcess::NormalExit) {
+        response.result = Utils::SynchronousProcessResponse::TerminatedAbnormally;
+    } else {
+        response.result = process->exitCode() == 0 ?
+                          Utils::SynchronousProcessResponse::Finished :
+                          Utils::SynchronousProcessResponse::FinishedError;
+    }
+    return response;
+}
+
+
 Utils::SynchronousProcessResponse
         VCSBasePlugin::runVCS(const QString &workingDir,
                               const QString &binary,
@@ -747,56 +814,66 @@ Utils::SynchronousProcessResponse
             nsp << "suppress_log";
         if (flags & ForceCLocale)
             nsp << "c_locale";
+        if (flags & FullySynchronously)
+            nsp << "fully_synchronously";
         if (outputCodec)
             nsp << " Codec: " << outputCodec->name();
     }
 
-    // Run, connect stderr to the output window
-    Utils::SynchronousProcess process;
-    if (!workingDir.isEmpty())
-        process.setWorkingDirectory(workingDir);
-
     VCSBase::VCSBasePlugin::setProcessEnvironment(&env, (flags & ForceCLocale));
-    process.setProcessEnvironment(env);
-    process.setTimeout(timeOutMS);
-    if (outputCodec)
-        process.setStdOutCodec(outputCodec);
-
-    // Suppress terminal on UNIX for ssh prompts if it is configured.
-    if (sshPromptConfigured && (flags & SshPasswordPrompt))
-        process.setFlags(Utils::SynchronousProcess::UnixTerminalDisabled);
-
-    // connect stderr to the output window if desired
-    if (flags & MergeOutputChannels) {
-        process.setProcessChannelMode(QProcess::MergedChannels);
+
+    Utils::SynchronousProcessResponse response;
+
+    if (flags & FullySynchronously) {
+        response = runVCS_FullySynchronously(workingDir, binary, arguments, timeOutMS,
+                                             env, flags, outputCodec);
     } else {
-        if (!(flags & SuppressStdErrInLogWindow)) {
-            process.setStdErrBufferedSignalsEnabled(true);
-            connect(&process, SIGNAL(stdErrBuffered(QString,bool)), outputWindow, SLOT(append(QString)));
+        // Run, connect stderr to the output window
+        Utils::SynchronousProcess process;
+        if (!workingDir.isEmpty())
+            process.setWorkingDirectory(workingDir);
+
+        process.setProcessEnvironment(env);
+        process.setTimeout(timeOutMS);
+        if (outputCodec)
+            process.setStdOutCodec(outputCodec);
+
+        // Suppress terminal on UNIX for ssh prompts if it is configured.
+        if (sshPromptConfigured && (flags & SshPasswordPrompt))
+            process.setFlags(Utils::SynchronousProcess::UnixTerminalDisabled);
+
+        // connect stderr to the output window if desired
+        if (flags & MergeOutputChannels) {
+            process.setProcessChannelMode(QProcess::MergedChannels);
+        } else {
+            if (!(flags & SuppressStdErrInLogWindow)) {
+                process.setStdErrBufferedSignalsEnabled(true);
+                connect(&process, SIGNAL(stdErrBuffered(QString,bool)), outputWindow, SLOT(append(QString)));
+            }
         }
-    }
 
-    // connect stdout to the output window if desired
-    if (flags & ShowStdOutInLogWindow) {
-        process.setStdOutBufferedSignalsEnabled(true);
-        connect(&process, SIGNAL(stdOutBuffered(QString,bool)), outputWindow, SLOT(append(QString)));
-    }
+        // connect stdout to the output window if desired
+        if (flags & ShowStdOutInLogWindow) {
+            process.setStdOutBufferedSignalsEnabled(true);
+            connect(&process, SIGNAL(stdOutBuffered(QString,bool)), outputWindow, SLOT(append(QString)));
+        }
 
-    process.setTimeOutMessageBoxEnabled(true);
+        process.setTimeOutMessageBoxEnabled(true);
 
-    // Run!
-    const Utils::SynchronousProcessResponse sp_resp = process.run(binary, arguments);
+        // Run!
+        response = process.run(binary, arguments);
+    }
 
     // Success/Fail message in appropriate window?
-    if (sp_resp.result == Utils::SynchronousProcessResponse::Finished) {
+    if (response.result == Utils::SynchronousProcessResponse::Finished) {
         if (flags & ShowSuccessMessage)
-            outputWindow->append(sp_resp.exitMessage(binary, timeOutMS));
+            outputWindow->append(response.exitMessage(binary, timeOutMS));
     } else {
         if (!(flags & SuppressFailMessageInLogWindow))
-            outputWindow->appendError(sp_resp.exitMessage(binary, timeOutMS));
+            outputWindow->appendError(response.exitMessage(binary, timeOutMS));
     }
 
-    return sp_resp;
+    return response;
 }
 } // namespace VCSBase
 
diff --git a/src/plugins/vcsbase/vcsbaseplugin.h b/src/plugins/vcsbase/vcsbaseplugin.h
index d9a22367981..466e4991727 100644
--- a/src/plugins/vcsbase/vcsbaseplugin.h
+++ b/src/plugins/vcsbase/vcsbaseplugin.h
@@ -192,7 +192,9 @@ public:
         SuppressFailMessageInLogWindow = 0x10, // No message VCS about failure in VCS output window.
         SuppressCommandLogging = 0x20, // No command log entry in VCS output window.
         ShowSuccessMessage = 0x40,      // Show message about successful completion in VCS output window.
-        ForceCLocale = 0x80             // Force C-locale for commands whose output is parsed.
+        ForceCLocale = 0x80,            // Force C-locale for commands whose output is parsed.
+        FullySynchronously = 0x100      // Suppress local event loop (in case UI actions are
+                                        // triggered by file watchers).
     };
 
     static Utils::SynchronousProcessResponse
-- 
GitLab