diff --git a/src/plugins/clearcase/clearcaseplugin.cpp b/src/plugins/clearcase/clearcaseplugin.cpp index cdb30368fb844dbfa2454c36bd95e3f0550725e1..fe36c0e449276e5c4124575a19bb95c7fee86a16 100644 --- a/src/plugins/clearcase/clearcaseplugin.cpp +++ b/src/plugins/clearcase/clearcaseplugin.cpp @@ -1377,7 +1377,6 @@ ClearCaseResponse const Utils::SynchronousProcessResponse sp_resp = VcsBase::VcsBasePlugin::runVcs(workingDir, executable, arguments, timeOut, - VcsBase::VcsBasePlugin::sshPrompt(), flags, outputCodec); response.error = sp_resp.result != Utils::SynchronousProcessResponse::Finished; diff --git a/src/plugins/cvs/cvsplugin.cpp b/src/plugins/cvs/cvsplugin.cpp index 1371e2418a4ea8c6ea0f0a4869eca41607f02743..f02744c5b96a3446ecb1155b493c105189387e0d 100644 --- a/src/plugins/cvs/cvsplugin.cpp +++ b/src/plugins/cvs/cvsplugin.cpp @@ -1237,7 +1237,7 @@ CvsResponse CvsPlugin::runCvs(const QString &workingDirectory, // Run, connect stderr to the output window const Utils::SynchronousProcessResponse sp_resp = runVcs(workingDirectory, executable, m_settings.addOptions(arguments), - timeOut, VcsBase::VcsBasePlugin::sshPrompt(), flags, outputCodec); + timeOut, flags, outputCodec); response.result = CvsResponse::OtherError; response.stdErr = sp_resp.stdErr; diff --git a/src/plugins/git/gitclient.cpp b/src/plugins/git/gitclient.cpp index 54c7e230213a9f7cae8be05e736b80372309fdfe..c7e5dfeac7dbfdf01cfb8f3b3a980ea9f9552e6e 100644 --- a/src/plugins/git/gitclient.cpp +++ b/src/plugins/git/gitclient.cpp @@ -2253,7 +2253,6 @@ VcsBase::Command *GitClient::executeGit(const QString &workingDirectory, outputWindow()->appendCommand(workingDirectory, settings()->stringValue(GitSettings::binaryPathKey), arguments); VcsBase::Command *command = createCommand(workingDirectory, editor, useOutputToWindow, editorLineNumber); command->addJob(arguments, settings()->intValue(GitSettings::timeoutKey)); - command->setUnixTerminalDisabled(false); if (expectChanges) command->addFlags(VcsBasePlugin::ExpectRepoChanges); command->execute(); @@ -2320,8 +2319,7 @@ Utils::SynchronousProcessResponse GitClient::synchronousGit(const QString &worki { return VcsBasePlugin::runVcs(workingDirectory, gitBinaryPath(), gitArguments, settings()->intValue(GitSettings::timeoutKey) * 1000, - processEnvironment(), VcsBase::VcsBasePlugin::sshPrompt(), - flags, outputCodec); + flags, outputCodec, processEnvironment()); } bool GitClient::fullySynchronousGit(const QString &workingDirectory, @@ -2330,10 +2328,11 @@ bool GitClient::fullySynchronousGit(const QString &workingDirectory, QByteArray* errorText, unsigned flags) const { - return VcsBasePlugin::runFullySynchronous(workingDirectory, gitBinaryPath(), gitArguments, - processEnvironment(), outputText, errorText, - settings()->intValue(GitSettings::timeoutKey) * 1000, - flags); + VcsBase::Command command(gitBinaryPath(), workingDirectory, processEnvironment()); + command.addFlags(flags); + return command.runFullySynchronous(gitArguments, + settings()->intValue(GitSettings::timeoutKey) * 1000, + outputText, errorText); } void GitClient::updateSubmodulesIfNeeded(const QString &workingDirectory, bool prompt) diff --git a/src/plugins/mercurial/mercurialclient.cpp b/src/plugins/mercurial/mercurialclient.cpp index 2a98a4ecb05a56755c356d73f92eaea83d319e2f..eef368c1d8b80cc3d7391e210861f35e409fe2fb 100644 --- a/src/plugins/mercurial/mercurialclient.cpp +++ b/src/plugins/mercurial/mercurialclient.cpp @@ -146,8 +146,7 @@ bool MercurialClient::synchronousPull(const QString &workingDir, const QString & QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); env.insert(QLatin1String("LANGUAGE"), QLatin1String("C")); const Utils::SynchronousProcessResponse resp = VcsBase::VcsBasePlugin::runVcs( - workingDir, binary, args, timeoutSec * 1000, env, - VcsBase::VcsBasePlugin::sshPrompt(), flags); + workingDir, binary, args, timeoutSec * 1000, flags, 0, env); const bool ok = resp.result == Utils::SynchronousProcessResponse::Finished; parsePullOutput(resp.stdOut.trimmed()); @@ -276,8 +275,6 @@ void MercurialClient::incoming(const QString &repositoryRoot, const QString &rep VcsBase::VcsBaseEditorWidget *editor = createVcsEditor(Constants::DIFFLOG, title, repositoryRoot, true, "incoming", id); VcsBase::Command *cmd = createCommand(repository, editor); - if (!repository.isEmpty() && VcsBase::VcsBasePlugin::isSshPromptConfigured()) - cmd->setUnixTerminalDisabled(true); enqueueJob(cmd, args); } @@ -293,7 +290,6 @@ void MercurialClient::outgoing(const QString &repositoryRoot) "outgoing", repositoryRoot); VcsBase::Command *cmd = createCommand(repositoryRoot, editor); - cmd->setUnixTerminalDisabled(VcsBase::VcsBasePlugin::isSshPromptConfigured()); enqueueJob(cmd, args); } diff --git a/src/plugins/subversion/subversionplugin.cpp b/src/plugins/subversion/subversionplugin.cpp index aff1a6e8b277f97b65ae80be4791ba34388b1cdf..4a173e3bf675d4704d8c03885fb6815f40c4c761 100644 --- a/src/plugins/subversion/subversionplugin.cpp +++ b/src/plugins/subversion/subversionplugin.cpp @@ -1120,8 +1120,7 @@ SubversionPlugin::Version SubversionPlugin::svnVersion() args << QLatin1String("--version") << QLatin1String("-q"); const Utils::SynchronousProcessResponse response = VcsBase::VcsBasePlugin::runVcs(QDir().absolutePath(), m_settings.binaryPath(), - args, m_settings.timeOutMs(), - VcsBase::VcsBasePlugin::sshPrompt(), 0); + args, m_settings.timeOutMs()); if (response.result == Utils::SynchronousProcessResponse::Finished && response.exitCode == 0) { m_svnVersionBinary = m_settings.binaryPath(); @@ -1156,7 +1155,7 @@ SubversionResponse SubversionPlugin::runSvn(const QString &workingDir, const QStringList completeArguments = SubversionPlugin::addAuthenticationOptions(arguments, userName, password); const Utils::SynchronousProcessResponse sp_resp = VcsBase::VcsBasePlugin::runVcs(workingDir, executable, completeArguments, timeOut, - VcsBase::VcsBasePlugin::sshPrompt(), flags, outputCodec); + flags, outputCodec); response.error = sp_resp.result != Utils::SynchronousProcessResponse::Finished; if (response.error) diff --git a/src/plugins/vcsbase/command.cpp b/src/plugins/vcsbase/command.cpp index a719c44d27bf1b82bd75c47175d2be1d0dddc3c6..42b6ccd6d2571dadd5fbd877d9391a8a984cb9a8 100644 --- a/src/plugins/vcsbase/command.cpp +++ b/src/plugins/vcsbase/command.cpp @@ -33,6 +33,7 @@ #include <coreplugin/icore.h> #include <coreplugin/progressmanager/progressmanager.h> #include <coreplugin/vcsmanager.h> +#include <vcsbase/vcsbaseoutputwindow.h> #include <utils/synchronousprocess.h> #include <utils/runextensions.h> @@ -49,6 +50,8 @@ Q_DECLARE_METATYPE(QVariant) +enum { debugExecution = 0 }; + namespace VcsBase { namespace Internal { @@ -70,7 +73,6 @@ public: const QString m_workingDirectory; const QProcessEnvironment m_environment; QVariant m_cookie; - bool m_unixTerminalDisabled; int m_defaultTimeout; unsigned m_flags; QTextCodec *m_codec; @@ -88,7 +90,6 @@ CommandPrivate::CommandPrivate(const QString &binary, m_binaryPath(binary), m_workingDirectory(workingDirectory), m_environment(environment), - m_unixTerminalDisabled(false), m_defaultTimeout(10), m_flags(0), m_codec(0), @@ -146,16 +147,6 @@ void Command::setDefaultTimeout(int timeout) d->m_defaultTimeout = timeout; } -bool Command::unixTerminalDisabled() const -{ - return d->m_unixTerminalDisabled; -} - -void Command::setUnixTerminalDisabled(bool e) -{ - d->m_unixTerminalDisabled = e; -} - unsigned Command::flags() const { return d->m_flags; @@ -220,12 +211,9 @@ void Command::run(QFutureInterface<void> &future) d->m_lastExecSuccess = true; for (int j = 0; j < count; j++) { const int timeOutSeconds = d->m_jobs.at(j).timeout; - Utils::SynchronousProcessResponse resp = - VcsBasePlugin::runVcs(d->m_workingDirectory, d->m_binaryPath, - d->m_jobs.at(j).arguments, - timeOutSeconds >= 0 ? timeOutSeconds * 1000 : -1, - d->m_environment, d->m_sshPasswordPrompt, - d->m_flags, d->m_codec); + Utils::SynchronousProcessResponse resp = runVcs( + d->m_jobs.at(j).arguments, + timeOutSeconds >= 0 ? timeOutSeconds * 1000 : -1); stdOut += resp.stdOut; stdErr += resp.stdErr; d->m_lastExecExitCode = resp.exitCode; @@ -248,6 +236,248 @@ void Command::run(QFutureInterface<void> &future) this->deleteLater(); } +class OutputProxy : public QObject +{ + Q_OBJECT + + friend class Command; + +public: + OutputProxy() + { + // Users of this class can either be in the GUI thread or in other threads. + // Use Qt::AutoConnection to always append in the GUI thread (directly or queued) + VcsBase::VcsBaseOutputWindow *outputWindow = VcsBase::VcsBaseOutputWindow::instance(); + connect(this, SIGNAL(append(QString)), outputWindow, SLOT(append(QString))); + connect(this, SIGNAL(appendSilently(QString)), outputWindow, SLOT(appendSilently(QString))); + connect(this, SIGNAL(appendError(QString)), outputWindow, SLOT(appendError(QString))); + connect(this, SIGNAL(appendCommand(QString,QString,QStringList)), + outputWindow, SLOT(appendCommand(QString,QString,QStringList))); + connect(this, SIGNAL(appendMessage(QString)), outputWindow, SLOT(appendMessage(QString))); + } + +signals: + void append(const QString &text); + void appendSilently(const QString &text); + void appendError(const QString &text); + void appendCommand(const QString &workingDirectory, + const QString &binary, + const QStringList &args); + void appendMessage(const QString &text); +}; + +Utils::SynchronousProcessResponse Command::runVcs(const QStringList &arguments, int timeoutMS) +{ + Utils::SynchronousProcessResponse response; + OutputProxy outputProxy; + + if (d->m_binaryPath.isEmpty()) { + response.result = Utils::SynchronousProcessResponse::StartFailed; + return response; + } + + VcsBase::VcsBaseOutputWindow *outputWindow = VcsBase::VcsBaseOutputWindow::instance(); + + if (!(d->m_flags & VcsBasePlugin::SuppressCommandLogging)) + emit outputProxy.appendCommand(d->m_workingDirectory, d->m_binaryPath, arguments); + + const bool sshPromptConfigured = !d->m_sshPasswordPrompt.isEmpty(); + if (debugExecution) { + QDebug nsp = qDebug().nospace(); + nsp << "Command::runVcs" << d->m_workingDirectory << d->m_binaryPath << arguments + << timeoutMS; + if (d->m_flags & VcsBasePlugin::ShowStdOutInLogWindow) + nsp << "stdout"; + if (d->m_flags & VcsBasePlugin::SuppressStdErrInLogWindow) + nsp << "suppress_stderr"; + if (d->m_flags & VcsBasePlugin::SuppressFailMessageInLogWindow) + nsp << "suppress_fail_msg"; + if (d->m_flags & VcsBasePlugin::MergeOutputChannels) + nsp << "merge_channels"; + if (d->m_flags & VcsBasePlugin::SshPasswordPrompt) + nsp << "ssh (" << sshPromptConfigured << ')'; + if (d->m_flags & VcsBasePlugin::SuppressCommandLogging) + nsp << "suppress_log"; + if (d->m_flags & VcsBasePlugin::ForceCLocale) + nsp << "c_locale"; + if (d->m_flags & VcsBasePlugin::FullySynchronously) + nsp << "fully_synchronously"; + if (d->m_flags & VcsBasePlugin::ExpectRepoChanges) + nsp << "expect_repo_changes"; + if (d->m_codec) + nsp << " Codec: " << d->m_codec->name(); + } + + // TODO tell the document manager about expected repository changes + // if (d->m_flags & ExpectRepoChanges) + // Core::DocumentManager::expectDirectoryChange(d->m_workingDirectory); + if (d->m_flags & VcsBasePlugin::FullySynchronously) { + response = runSynchronous(arguments, timeoutMS); + } else { + // Run, connect stderr to the output window + Utils::SynchronousProcess process; + if (!d->m_workingDirectory.isEmpty()) + process.setWorkingDirectory(d->m_workingDirectory); + + QProcessEnvironment env = d->m_environment; + VcsBasePlugin::setProcessEnvironment(&env, + (d->m_flags & VcsBasePlugin::ForceCLocale), + d->m_sshPasswordPrompt); + process.setProcessEnvironment(env); + process.setTimeout(timeoutMS); + if (d->m_codec) + process.setCodec(d->m_codec); + + // Suppress terminal on UNIX for ssh prompts if it is configured. + if (sshPromptConfigured && (d->m_flags & VcsBasePlugin::SshPasswordPrompt)) + process.setFlags(Utils::SynchronousProcess::UnixTerminalDisabled); + + // connect stderr to the output window if desired + if (d->m_flags & VcsBasePlugin::MergeOutputChannels) { + process.setProcessChannelMode(QProcess::MergedChannels); + } else if (!(d->m_flags & VcsBasePlugin::SuppressStdErrInLogWindow)) { + process.setStdErrBufferedSignalsEnabled(true); + connect(&process, SIGNAL(stdErrBuffered(QString,bool)), outputWindow, SLOT(appendError(QString))); + } + + // connect stdout to the output window if desired + if (d->m_flags & VcsBasePlugin::ShowStdOutInLogWindow) { + process.setStdOutBufferedSignalsEnabled(true); + connect(&process, SIGNAL(stdOutBuffered(QString,bool)), outputWindow, SLOT(append(QString))); + } + + process.setTimeOutMessageBoxEnabled(true); + + // Run! + response = process.run(d->m_binaryPath, arguments); + } + + // Success/Fail message in appropriate window? + if (response.result == Utils::SynchronousProcessResponse::Finished) { + if (d->m_flags & VcsBasePlugin::ShowSuccessMessage) + emit outputProxy.appendMessage(response.exitMessage(d->m_binaryPath, timeoutMS)); + } else if (!(d->m_flags & VcsBasePlugin::SuppressFailMessageInLogWindow)) { + emit outputProxy.appendError(response.exitMessage(d->m_binaryPath, timeoutMS)); + } + if (d->m_flags & VcsBasePlugin::ExpectRepoChanges) { + // TODO tell the document manager that the directory now received all expected changes + // Core::DocumentManager::unexpectDirectoryChange(d->m_workingDirectory); + Core::ICore::vcsManager()->emitRepositoryChanged(d->m_workingDirectory); + } + + return response; +} + +Utils::SynchronousProcessResponse Command::runSynchronous(const QStringList &arguments, int timeoutMS) +{ + Utils::SynchronousProcessResponse response; + + // Set up process + unsigned processFlags = 0; + if (!d->m_sshPasswordPrompt.isEmpty() && (d->m_flags & VcsBasePlugin::SshPasswordPrompt)) + processFlags |= Utils::SynchronousProcess::UnixTerminalDisabled; + QSharedPointer<QProcess> process = Utils::SynchronousProcess::createProcess(processFlags); + if (!d->m_workingDirectory.isEmpty()) + process->setWorkingDirectory(d->m_workingDirectory); + QProcessEnvironment env = d->m_environment; + VcsBasePlugin::setProcessEnvironment(&env, + (d->m_flags & VcsBasePlugin::ForceCLocale), + d->m_sshPasswordPrompt); + process->setProcessEnvironment(env); + if (d->m_flags & VcsBasePlugin::MergeOutputChannels) + process->setProcessChannelMode(QProcess::MergedChannels); + + // Start + process->start(d->m_binaryPath, arguments, QIODevice::ReadOnly); + process->closeWriteChannel(); + 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); + + OutputProxy outputProxy; + if (!stdErr.isEmpty()) { + response.stdErr = Utils::SynchronousProcess::normalizeNewlines( + d->m_codec ? d->m_codec->toUnicode(stdErr) : QString::fromLocal8Bit(stdErr)); + if (!(d->m_flags & VcsBasePlugin::SuppressStdErrInLogWindow)) + emit outputProxy.append(response.stdErr); + } + + if (!stdOut.isEmpty()) { + response.stdOut = Utils::SynchronousProcess::normalizeNewlines( + d->m_codec ? d->m_codec->toUnicode(stdOut) : QString::fromLocal8Bit(stdOut)); + if (d->m_flags & VcsBasePlugin::ShowStdOutInLogWindow) { + if (d->m_flags & VcsBasePlugin::SilentOutput) + emit outputProxy.appendSilently(response.stdOut); + else + emit outputProxy.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; +} + +bool Command::runFullySynchronous(const QStringList &arguments, int timeoutMS, + QByteArray *outputData, QByteArray *errorData) +{ + if (d->m_binaryPath.isEmpty()) + return false; + + OutputProxy outputProxy; + if (!(d->m_flags & VcsBasePlugin::SuppressCommandLogging)) + emit outputProxy.appendCommand(d->m_workingDirectory, d->m_binaryPath, arguments); + + // TODO tell the document manager about expected repository changes + // if (d->m_flags & ExpectRepoChanges) + // Core::DocumentManager::expectDirectoryChange(workingDirectory); + QProcess process; + process.setWorkingDirectory(d->m_workingDirectory); + process.setProcessEnvironment(d->m_environment); + + process.start(d->m_binaryPath, arguments); + process.closeWriteChannel(); + if (!process.waitForStarted()) { + if (errorData) { + const QString msg = QString::fromLatin1("Unable to execute '%1': %2:") + .arg(d->m_binaryPath, process.errorString()); + *errorData = msg.toLocal8Bit(); + } + return false; + } + + if (!Utils::SynchronousProcess::readDataFromProcess(process, timeoutMS, outputData, errorData, true)) { + if (errorData) + errorData->append(tr("Error: Executable timed out after %1s.").arg(timeoutMS / 1000).toLocal8Bit()); + Utils::SynchronousProcess::stopProcess(process); + return false; + } + + if (d->m_flags & VcsBasePlugin::ExpectRepoChanges) { + // TODO tell the document manager that the directory now received all expected changes + // Core::DocumentManager::unexpectDirectoryChange(workingDirectory); + Core::ICore::vcsManager()->emitRepositoryChanged(d->m_workingDirectory); + } + + return process.exitStatus() == QProcess::NormalExit && process.exitCode() == 0; +} + const QVariant &Command::cookie() const { return d->m_cookie; @@ -269,3 +499,5 @@ void Command::setCodec(QTextCodec *codec) } } // namespace VcsBase + +#include "command.moc" diff --git a/src/plugins/vcsbase/command.h b/src/plugins/vcsbase/command.h index 32a2bee5f7af474704bdcfa3b698432840feb39d..84b4d7c398cd0e1881691479e0f8ee4818163ef1 100644 --- a/src/plugins/vcsbase/command.h +++ b/src/plugins/vcsbase/command.h @@ -32,6 +32,8 @@ #include "vcsbase_global.h" +#include <utils/synchronousprocess.h> + #include <QObject> QT_BEGIN_NAMESPACE @@ -69,10 +71,6 @@ public: int defaultTimeout() const; void setDefaultTimeout(int timeout); - // Disable Terminal on UNIX (see VCS SSH handling) - bool unixTerminalDisabled() const; - void setUnixTerminalDisabled(bool); - unsigned flags() const; void addFlags(unsigned f); @@ -82,8 +80,15 @@ public: QTextCodec *codec() const; void setCodec(QTextCodec *codec); + + Utils::SynchronousProcessResponse runVcs(const QStringList &arguments, int timeoutMS); + // Make sure to not pass through the event loop at all: + bool runFullySynchronous(const QStringList &arguments, int timeoutMS, + QByteArray *outputData, QByteArray *errorData); + private: void run(QFutureInterface<void> &future); + Utils::SynchronousProcessResponse runSynchronous(const QStringList &arguments, int timeoutMS); signals: void output(const QString &); diff --git a/src/plugins/vcsbase/vcsbaseclient.cpp b/src/plugins/vcsbase/vcsbaseclient.cpp index 0b3a4a28e623204d69e91b8e1085791223851b20..a35aeafdc3fa14fe76ec2bcf084069f340861b8e 100644 --- a/src/plugins/vcsbase/vcsbaseclient.cpp +++ b/src/plugins/vcsbase/vcsbaseclient.cpp @@ -319,7 +319,7 @@ Utils::SynchronousProcessResponse VcsBaseClient::vcsSynchronousExec( const QString binary = settings()->binaryPath(); const int timeoutSec = settings()->intValue(VcsBaseClientSettings::timeoutKey); return VcsBase::VcsBasePlugin::runVcs(workingDirectory, binary, args, timeoutSec * 1000, - VcsBase::VcsBasePlugin::sshPrompt(), flags, outputCodec); + flags, outputCodec); } void VcsBaseClient::annotate(const QString &workingDir, const QString &file, @@ -492,7 +492,6 @@ void VcsBaseClient::update(const QString &repositoryRoot, const QString &revisio args << revisionSpec(revision) << extraOptions; Command *cmd = createCommand(repositoryRoot); cmd->setCookie(repositoryRoot); - cmd->setUnixTerminalDisabled(VcsBase::VcsBasePlugin::isSshPromptConfigured()); connect(cmd, SIGNAL(success(QVariant)), this, SIGNAL(changed(QVariant)), Qt::QueuedConnection); enqueueJob(cmd, args); } diff --git a/src/plugins/vcsbase/vcsbaseplugin.cpp b/src/plugins/vcsbase/vcsbaseplugin.cpp index a33c054f3c89062d8dc16725249ccf837516dad5..0b1247e8923bcda6e2f70c19b6c760db5b559a0d 100644 --- a/src/plugins/vcsbase/vcsbaseplugin.cpp +++ b/src/plugins/vcsbase/vcsbaseplugin.cpp @@ -33,6 +33,7 @@ #include "commonvcssettings.h" #include "vcsbaseoutputwindow.h" #include "corelistener.h" +#include "command.h" #include <coreplugin/documentmanager.h> #include <coreplugin/icore.h> @@ -59,7 +60,7 @@ using namespace Utils; -enum { debug = 0, debugRepositorySearch = 0, debugExecution = 0 }; +enum { debug = 0, debugRepositorySearch = 0 }; /*! \namespace VcsBase @@ -468,36 +469,6 @@ VCSBASE_EXPORT QDebug operator<<(QDebug in, const VcsBasePluginState &state) return in; } -class OutputProxy : public QObject -{ - Q_OBJECT - - friend class VcsBasePlugin; - -public: - OutputProxy() - { - // Users of this class can either be in the GUI thread or in other threads. - // Use Qt::AutoConnection to always append in the GUI thread (directly or queued) - VcsBase::VcsBaseOutputWindow *outputWindow = VcsBase::VcsBaseOutputWindow::instance(); - connect(this, SIGNAL(append(QString)), outputWindow, SLOT(append(QString))); - connect(this, SIGNAL(appendSilently(QString)), outputWindow, SLOT(appendSilently(QString))); - connect(this, SIGNAL(appendError(QString)), outputWindow, SLOT(appendError(QString))); - connect(this, SIGNAL(appendCommand(QString,QString,QStringList)), - outputWindow, SLOT(appendCommand(QString,QString,QStringList))); - connect(this, SIGNAL(appendMessage(QString)), outputWindow, SLOT(appendMessage(QString))); - } - -signals: - void append(const QString &text); - void appendSilently(const QString &text); - void appendError(const QString &text); - void appendCommand(const QString &workingDirectory, - const QString &binary, - const QStringList &args); - void appendMessage(const QString &text); -}; - /*! \class VcsBase::VcsBasePlugin @@ -838,11 +809,6 @@ bool VcsBasePlugin::isSshPromptConfigured() return !sshPrompt().isEmpty(); } -void VcsBasePlugin::setProcessEnvironment(QProcessEnvironment *e, bool forceCLocale) -{ - setProcessEnvironment(e, forceCLocale, sshPrompt()); -} - void VcsBasePlugin::setProcessEnvironment(QProcessEnvironment *e, bool forceCLocale, const QString &sshPromptBinary) @@ -853,254 +819,21 @@ void VcsBasePlugin::setProcessEnvironment(QProcessEnvironment *e, e->insert(QLatin1String("SSH_ASKPASS"), sshPromptBinary); } -// Run a process fully synchronously, returning Utils::SynchronousProcessResponse +// Run a process synchronously, returning Utils::SynchronousProcessResponse // response struct and using the VcsBasePlugin flags as applicable -SynchronousProcessResponse VcsBasePlugin::runVcsFullySynchronously( - const QString &workingDir, - const QString &binary, - const QStringList &arguments, - int timeOutMS, - QProcessEnvironment env, - const QString &sshPasswordPrompt, - unsigned flags, - QTextCodec *outputCodec) -{ - SynchronousProcessResponse response; - if (binary.isEmpty()) { - response.result = SynchronousProcessResponse::StartFailed; - return response; - } - - // Set up process - unsigned processFlags = 0; - if (!sshPasswordPrompt.isEmpty() && (flags & VcsBasePlugin::SshPasswordPrompt)) - processFlags |= SynchronousProcess::UnixTerminalDisabled; - QSharedPointer<QProcess> process = 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, QIODevice::ReadOnly); - process->closeWriteChannel(); - if (!process->waitForStarted()) { - response.result = SynchronousProcessResponse::StartFailed; - return response; - } - - // process output - QByteArray stdOut; - QByteArray stdErr; - const bool timedOut = - !SynchronousProcess::readDataFromProcess(*process.data(), timeOutMS, - &stdOut, &stdErr, true); - - OutputProxy output; - if (!stdErr.isEmpty()) { - response.stdErr = Utils::SynchronousProcess::normalizeNewlines( - outputCodec ? outputCodec->toUnicode(stdErr) : QString::fromLocal8Bit(stdErr)); - if (!(flags & VcsBasePlugin::SuppressStdErrInLogWindow)) - emit output.append(response.stdErr); - } - - if (!stdOut.isEmpty()) { - response.stdOut = Utils::SynchronousProcess::normalizeNewlines( - outputCodec ? outputCodec->toUnicode(stdOut) : QString::fromLocal8Bit(stdOut)); - if (flags & VcsBasePlugin::ShowStdOutInLogWindow) { - if (flags & VcsBasePlugin::SilentOutput) - emit output.appendSilently(response.stdOut); - else - emit output.append(response.stdOut); - } - } - - // Result - if (timedOut) { - response.result = SynchronousProcessResponse::Hang; - } else if (process->exitStatus() != QProcess::NormalExit) { - response.result = SynchronousProcessResponse::TerminatedAbnormally; - } else { - response.result = process->exitCode() == 0 ? - SynchronousProcessResponse::Finished : - SynchronousProcessResponse::FinishedError; - } - return response; -} - - -SynchronousProcessResponse VcsBasePlugin::runVcs(const QString &workingDir, - const QString &binary, - const QStringList &arguments, - int timeOutMS, - const QString &sshPasswordPrompt, - unsigned flags, - QTextCodec *outputCodec) -{ - const QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); - return runVcs(workingDir, binary, arguments, timeOutMS, env, - sshPasswordPrompt, flags, outputCodec); -} - SynchronousProcessResponse VcsBasePlugin::runVcs(const QString &workingDir, const QString &binary, const QStringList &arguments, int timeOutMS, - QProcessEnvironment env, - const QString &sshPasswordPrompt, unsigned flags, - QTextCodec *outputCodec) -{ - SynchronousProcessResponse response; - OutputProxy output; - - if (binary.isEmpty()) { - response.result = SynchronousProcessResponse::StartFailed; - return response; - } - - VcsBase::VcsBaseOutputWindow *outputWindow = VcsBase::VcsBaseOutputWindow::instance(); - - if (!(flags & SuppressCommandLogging)) - emit output.appendCommand(workingDir, binary, arguments); - - const bool sshPromptConfigured = !sshPasswordPrompt.isEmpty(); - if (debugExecution) { - QDebug nsp = qDebug().nospace(); - nsp << "VcsBasePlugin::runVcs" << workingDir << binary << arguments - << timeOutMS; - if (flags & ShowStdOutInLogWindow) - nsp << "stdout"; - if (flags & SuppressStdErrInLogWindow) - nsp << "suppress_stderr"; - if (flags & SuppressFailMessageInLogWindow) - nsp << "suppress_fail_msg"; - if (flags & MergeOutputChannels) - nsp << "merge_channels"; - if (flags & SshPasswordPrompt) - nsp << "ssh (" << sshPromptConfigured << ')'; - if (flags & SuppressCommandLogging) - nsp << "suppress_log"; - if (flags & ForceCLocale) - nsp << "c_locale"; - if (flags & FullySynchronously) - nsp << "fully_synchronously"; - if (flags & ExpectRepoChanges) - nsp << "expect_repo_changes"; - if (outputCodec) - nsp << " Codec: " << outputCodec->name(); - } - - setProcessEnvironment(&env, (flags & ForceCLocale), sshPasswordPrompt); - - // TODO tell the document manager about expected repository changes - // if (flags & ExpectRepoChanges) - // Core::DocumentManager::expectDirectoryChange(workingDir); - if (flags & FullySynchronously) { - response = runVcsFullySynchronously(workingDir, binary, arguments, timeOutMS, - env, sshPasswordPrompt, flags, outputCodec); - } else { - // Run, connect stderr to the output window - SynchronousProcess process; - if (!workingDir.isEmpty()) - process.setWorkingDirectory(workingDir); - - process.setProcessEnvironment(env); - process.setTimeout(timeOutMS); - if (outputCodec) - process.setCodec(outputCodec); - - // Suppress terminal on UNIX for ssh prompts if it is configured. - if (sshPromptConfigured && (flags & SshPasswordPrompt)) - process.setFlags(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))); - } - - process.setTimeOutMessageBoxEnabled(true); - - // Run! - response = process.run(binary, arguments); - } - - // Success/Fail message in appropriate window? - if (response.result == SynchronousProcessResponse::Finished) { - if (flags & ShowSuccessMessage) - emit output.appendMessage(response.exitMessage(binary, timeOutMS)); - } else if (!(flags & SuppressFailMessageInLogWindow)) { - emit output.appendError(response.exitMessage(binary, timeOutMS)); - } - if (flags & ExpectRepoChanges) { - // TODO tell the document manager that the directory now received all expected changes - // Core::DocumentManager::unexpectDirectoryChange(workingDir); - Core::ICore::vcsManager()->emitRepositoryChanged(workingDir); - } - - return response; -} - -bool VcsBasePlugin::runFullySynchronous(const QString &workingDirectory, - const QString &binary, - const QStringList &arguments, - const QProcessEnvironment &env, - QByteArray* outputText, - QByteArray* errorText, - int timeoutMS, - unsigned flags) -{ - if (binary.isEmpty()) - return false; - - OutputProxy output; - if (!(flags & SuppressCommandLogging)) - emit output.appendCommand(workingDirectory, binary, arguments); - - // TODO tell the document manager about expected repository changes - // if (flags & ExpectRepoChanges) - // Core::DocumentManager::expectDirectoryChange(workingDirectory); - QProcess process; - process.setWorkingDirectory(workingDirectory); - process.setProcessEnvironment(env); - - process.start(binary, arguments); - process.closeWriteChannel(); - if (!process.waitForStarted()) { - if (errorText) { - const QString msg = QString::fromLatin1("Unable to execute '%1': %2:") - .arg(binary, process.errorString()); - *errorText = msg.toLocal8Bit(); - } - return false; - } - - if (!SynchronousProcess::readDataFromProcess(process, timeoutMS, outputText, errorText, true)) { - if (errorText) - errorText->append(tr("Error: Executable timed out after %1s.").arg(timeoutMS / 1000).toLocal8Bit()); - SynchronousProcess::stopProcess(process); - return false; - } - - if (flags & ExpectRepoChanges) { - // TODO tell the document manager that the directory now received all expected changes - // Core::DocumentManager::unexpectDirectoryChange(workingDirectory); - Core::ICore::vcsManager()->emitRepositoryChanged(workingDirectory); - } - - return process.exitStatus() == QProcess::NormalExit && process.exitCode() == 0; + QTextCodec *outputCodec, + const QProcessEnvironment &env) +{ + Command command(binary, workingDir, + env.isEmpty() ? QProcessEnvironment::systemEnvironment() : env); + command.addFlags(flags); + command.setCodec(outputCodec); + return command.runVcs(arguments, timeOutMS); } bool VcsBasePlugin::runPatch(const QByteArray &input, const QString &workingDirectory, diff --git a/src/plugins/vcsbase/vcsbaseplugin.h b/src/plugins/vcsbase/vcsbaseplugin.h index 5df80930eea32e22792218518c64fc127ce4fe05..5799f8bf991a2ac612853a0ac9b115306f4304f3 100644 --- a/src/plugins/vcsbase/vcsbaseplugin.h +++ b/src/plugins/vcsbase/vcsbaseplugin.h @@ -36,10 +36,10 @@ #include <QSharedDataPointer> #include <QList> +#include <QProcessEnvironment> QT_BEGIN_NAMESPACE class QAction; -class QProcessEnvironment; class QTextCodec; QT_END_NAMESPACE @@ -150,7 +150,9 @@ public: // Sets up SSH graphical password prompting (note that the latter // requires a terminal-less process) and sets LANG to 'C' to force English // (suppress LOCALE warnings/parse commands output) if desired. - static void setProcessEnvironment(QProcessEnvironment *e, bool forceCLocale); + static void setProcessEnvironment(QProcessEnvironment *e, + bool forceCLocale, + const QString &sshPasswordPrompt = sshPrompt()); // Returns SSH prompt configured in settings. static QString sshPrompt(); // Returns whether an SSH prompt is configured. @@ -176,28 +178,9 @@ public: const QString &binary, const QStringList &arguments, int timeOutMS, - QProcessEnvironment env, - const QString &sshPasswordPrompt, unsigned flags = 0, - QTextCodec *outputCodec = 0); - - static Utils::SynchronousProcessResponse runVcs(const QString &workingDir, - const QString &binary, - const QStringList &arguments, - int timeOutMS, - const QString &sshPasswordPrompt, - unsigned flags = 0, - QTextCodec *outputCodec = 0); - - // Make sure to not pass through the event loop at all: - static bool runFullySynchronous(const QString &workingDirectory, - const QString &binary, - const QStringList &arguments, - const QProcessEnvironment &env, - QByteArray* outputText, - QByteArray *errorText, - int timeoutMS, - unsigned flags); + QTextCodec *outputCodec = 0, + const QProcessEnvironment &env = QProcessEnvironment()); // Utility to run the 'patch' command static bool runPatch(const QByteArray &input, const QString &workingDirectory = QString(), @@ -245,20 +228,6 @@ private slots: void slotTestRemoveSnapshot(); private: - static void setProcessEnvironment(QProcessEnvironment *e, - bool forceCLocale, - const QString &sshPasswordPrompt); - - static Utils::SynchronousProcessResponse runVcsFullySynchronously( - const QString &workingDir, - const QString &binary, - const QStringList &arguments, - int timeOutMS, - QProcessEnvironment env, - const QString &sshPasswordPrompt, - unsigned flags, - QTextCodec *outputCodec = 0); - VcsBasePluginPrivate *d; };