diff --git a/src/plugins/cvs/cvsplugin.cpp b/src/plugins/cvs/cvsplugin.cpp index b7f0f461fb0467b3aa040080768eabecd5baf410..ce449df8e16a2c8e1ca5ed86a56909776ceff8e2 100644 --- a/src/plugins/cvs/cvsplugin.cpp +++ b/src/plugins/cvs/cvsplugin.cpp @@ -473,7 +473,8 @@ void CVSPlugin::cvsDiff(const QString &workingDir, const QStringList &files) // CVS returns the diff exit code (1 if files differ), which is // undistinguishable from a "file not found" error, unfortunately. - const CVSResponse response = runCVS(workingDir, args, m_settings.timeOutMS(), false, codec); + const CVSResponse response = + runCVS(workingDir, args, m_settings.timeOutMS(), 0, codec); switch (response.result) { case CVSResponse::NonNullExitCode: case CVSResponse::Ok: @@ -560,7 +561,9 @@ void CVSPlugin::revertAll() return; QStringList args; args << QLatin1String("update") << QLatin1String("-C") << state.topLevel(); - const CVSResponse revertResponse = runCVS(state.topLevel(), args, m_settings.timeOutMS(), true); + const CVSResponse revertResponse = + runCVS(state.topLevel(), args, m_settings.timeOutMS(), + SshPasswordPrompt|ShowStdOutInLogWindow); if (revertResponse.result == CVSResponse::Ok) { cvsVersionControl()->emitRepositoryChanged(state.topLevel()); } else { @@ -574,7 +577,8 @@ void CVSPlugin::revertCurrentFile() QTC_ASSERT(state.hasFile(), return) QStringList args; args << QLatin1String("diff") << state.relativeCurrentFile(); - const CVSResponse diffResponse = runCVS(state.currentFileTopLevel(), args, m_settings.timeOutMS(), false); + const CVSResponse diffResponse = + runCVS(state.currentFileTopLevel(), args, m_settings.timeOutMS(), 0); switch (diffResponse.result) { case CVSResponse::Ok: return; // Not modified, diff exit code 0 @@ -595,7 +599,9 @@ void CVSPlugin::revertCurrentFile() // revert args.clear(); args << QLatin1String("update") << QLatin1String("-C") << state.relativeCurrentFile(); - const CVSResponse revertResponse = runCVS(state.currentFileTopLevel(), args, m_settings.timeOutMS(), true); + const CVSResponse revertResponse = + runCVS(state.currentFileTopLevel(), args, m_settings.timeOutMS(), + SshPasswordPrompt|ShowStdOutInLogWindow); if (revertResponse.result == CVSResponse::Ok) { cvsVersionControl()->emitFilesChanged(QStringList(state.currentFile())); } @@ -644,7 +650,8 @@ void CVSPlugin::startCommit(const QString &workingDir, const QStringList &files) // We need the "Examining <subdir>" stderr output to tell // where we are, so, have stdout/stderr channels merged. QStringList args = QStringList(QLatin1String("status")); - const CVSResponse response = runCVS(workingDir, args, m_settings.timeOutMS(), false, 0, true); + const CVSResponse response = + runCVS(workingDir, args, m_settings.timeOutMS(), MergeOutputChannels); if (response.result != CVSResponse::Ok) return; // Get list of added/modified/deleted files and purge out undesired ones @@ -693,7 +700,9 @@ bool CVSPlugin::commit(const QString &messageFile, QStringList args = QStringList(QLatin1String("commit")); args << QLatin1String("-F") << messageFile; args.append(fileList); - const CVSResponse response = runCVS(m_commitRepository, args, m_settings.longTimeOutMS(), true); + const CVSResponse response = + runCVS(m_commitRepository, args, m_settings.longTimeOutMS(), + SshPasswordPrompt|ShowStdOutInLogWindow); return response.result == CVSResponse::Ok ; } @@ -729,7 +738,9 @@ void CVSPlugin::filelog(const QString &workingDir, QStringList args; args << QLatin1String("log"); args.append(files); - const CVSResponse response = runCVS(workingDir, args, m_settings.timeOutMS(), false, codec); + const CVSResponse response = + runCVS(workingDir, args, m_settings.timeOutMS(), + SshPasswordPrompt, codec); if (response.result != CVSResponse::Ok) return; @@ -755,7 +766,9 @@ void CVSPlugin::updateProject() QStringList args(QLatin1String("update")); args.push_back(QLatin1String("-dR")); args.append(state.relativeCurrentProject()); - const CVSResponse response = runCVS(state.currentProjectTopLevel(), args, m_settings.longTimeOutMS(), true); + const CVSResponse response = + runCVS(state.currentProjectTopLevel(), args, m_settings.longTimeOutMS(), + SshPasswordPrompt|ShowStdOutInLogWindow); if (response.result == CVSResponse::Ok) cvsVersionControl()->emitRepositoryChanged(state.currentProjectTopLevel()); } @@ -786,7 +799,9 @@ void CVSPlugin::annotate(const QString &workingDir, const QString &file, if (!revision.isEmpty()) args << QLatin1String("-r") << revision; args << file; - const CVSResponse response = runCVS(workingDir, args, m_settings.timeOutMS(), false, codec); + const CVSResponse response = + runCVS(workingDir, args, m_settings.timeOutMS(), + SshPasswordPrompt, codec); if (response.result != CVSResponse::Ok) return; @@ -813,7 +828,8 @@ void CVSPlugin::projectStatus() QTC_ASSERT(state.hasProject(), return) QStringList args; args << QLatin1String("status") << state.relativeCurrentProject(); - const CVSResponse response = runCVS(state.currentProjectTopLevel(), args, m_settings.timeOutMS(), false); + const CVSResponse response = + runCVS(state.currentProjectTopLevel(), args, m_settings.timeOutMS(), 0); if (response.result == CVSResponse::Ok) showOutputInEditor(tr("Project status"), response.stdOut, VCSBase::RegularCommandOutput, state.currentProjectTopLevel(), 0); } @@ -857,7 +873,8 @@ bool CVSPlugin::describe(const QString &toplevel, const QString &file, const // Run log to obtain commit id and details QStringList args; args << QLatin1String("log") << (QLatin1String("-r") + changeNr) << file; - const CVSResponse logResponse = runCVS(toplevel, args, m_settings.timeOutMS(), false); + const CVSResponse logResponse = + runCVS(toplevel, args, m_settings.timeOutMS(), SshPasswordPrompt); if (logResponse.result != CVSResponse::Ok) { *errorMessage = logResponse.message; return false; @@ -878,7 +895,8 @@ bool CVSPlugin::describe(const QString &toplevel, const QString &file, const args.clear(); args << QLatin1String("log") << QLatin1String("-d") << (dateS + QLatin1Char('<') + nextDayS); - const CVSResponse repoLogResponse = runCVS(toplevel, args, m_settings.longTimeOutMS(), false); + const CVSResponse repoLogResponse = + runCVS(toplevel, args, m_settings.longTimeOutMS(), SshPasswordPrompt); if (repoLogResponse.result != CVSResponse::Ok) { *errorMessage = repoLogResponse.message; return false; @@ -914,7 +932,8 @@ bool CVSPlugin::describe(const QString &repositoryPath, // Run log QStringList args(QLatin1String("log")); args << (QLatin1String("-r") + it->revisions.front().revision) << it->file; - const CVSResponse logResponse = runCVS(repositoryPath, args, m_settings.timeOutMS(), false); + const CVSResponse logResponse = + runCVS(repositoryPath, args, m_settings.timeOutMS(), SshPasswordPrompt); if (logResponse.result != CVSResponse::Ok) { *errorMessage = logResponse.message; return false; @@ -930,7 +949,8 @@ bool CVSPlugin::describe(const QString &repositoryPath, args << m_settings.cvsDiffOptions << QLatin1String("-r") << previousRev << QLatin1String("-r") << it->revisions.front().revision << it->file; - const CVSResponse diffResponse = runCVS(repositoryPath, args, m_settings.timeOutMS(), false, codec); + const CVSResponse diffResponse = + runCVS(repositoryPath, args, m_settings.timeOutMS(), 0, codec); switch (diffResponse.result) { case CVSResponse::Ok: case CVSResponse::NonNullExitCode: // Diff exit code != 0 @@ -970,29 +990,13 @@ void CVSPlugin::submitCurrentLog() << Core::EditorManager::instance()->currentEditor()); } -static inline QString processStdErr(QProcess &proc) -{ - return QString::fromLocal8Bit(proc.readAllStandardError()).remove(QLatin1Char('\r')); -} - -// Format log entry for command -static inline QString msgExecutionLogEntry(const QString &workingDir, const QString &executable, const QStringList &arguments) -{ - //: Executing: <executable> <arguments> - const QString args = arguments.join(QString(QLatin1Char(' '))); - if (workingDir.isEmpty()) - return CVSPlugin::tr("Executing: %1 %2\n").arg(executable, args); - return CVSPlugin::tr("Executing in %1: %2 %3\n"). - arg(QDir::toNativeSeparators(workingDir), executable, args); -} - // Run CVS. At this point, file arguments must be relative to // the working directory (see above). CVSResponse CVSPlugin::runCVS(const QString &workingDirectory, const QStringList &arguments, int timeOut, - bool showStdOutInOutputWindow, QTextCodec *outputCodec, - bool mergeStderr) + unsigned flags, + QTextCodec *outputCodec) { const QString executable = m_settings.cvsCommand; CVSResponse response; @@ -1001,37 +1005,12 @@ CVSResponse CVSPlugin::runCVS(const QString &workingDirectory, response.message =tr("No cvs executable specified!"); return response; } - // Fix files and compile complete arguments - const QStringList allArgs = m_settings.addOptions(arguments); - - const QString outputText = msgExecutionLogEntry(workingDirectory, executable, allArgs); - VCSBase::VCSBaseOutputWindow::instance()->appendCommand(outputText); - - if (CVS::Constants::debug) - qDebug() << "runCVS" << timeOut << outputText; - // Run, connect stderr to the output window - Utils::SynchronousProcess process; - if (!workingDirectory.isEmpty()) - process.setWorkingDirectory(workingDirectory); - - if (mergeStderr) - process.setProcessChannelMode(QProcess::MergedChannels); - - process.setTimeout(timeOut); - process.setStdOutCodec(outputCodec); + const Utils::SynchronousProcessResponse sp_resp = + runVCS(workingDirectory, executable, + m_settings.addOptions(arguments), + timeOut, flags, outputCodec); - process.setStdErrBufferedSignalsEnabled(true); - VCSBase::VCSBaseOutputWindow *outputWindow = VCSBase::VCSBaseOutputWindow::instance(); - connect(&process, SIGNAL(stdErrBuffered(QString,bool)), outputWindow, SLOT(append(QString))); - - // connect stdout to the output window if desired - if (showStdOutInOutputWindow) { - process.setStdOutBufferedSignalsEnabled(true); - connect(&process, SIGNAL(stdOutBuffered(QString,bool)), outputWindow, SLOT(append(QString))); - } - - const Utils::SynchronousProcessResponse sp_resp = process.run(executable, allArgs); response.result = CVSResponse::OtherError; response.stdErr = sp_resp.stdErr; response.stdOut = sp_resp.stdOut; @@ -1041,20 +1020,15 @@ CVSResponse CVSPlugin::runCVS(const QString &workingDirectory, break; case Utils::SynchronousProcessResponse::FinishedError: response.result = CVSResponse::NonNullExitCode; - response.message = tr("The process terminated with exit code %1.").arg(sp_resp.exitCode); break; case Utils::SynchronousProcessResponse::TerminatedAbnormally: - response.message = tr("The process terminated abnormally."); - break; case Utils::SynchronousProcessResponse::StartFailed: - response.message = tr("Could not start cvs '%1'. Please check your settings in the preferences.").arg(executable); - break; case Utils::SynchronousProcessResponse::Hang: - response.message = tr("CVS did not respond within timeout limit (%1 ms).").arg(timeOut); break; } + if (response.result != CVSResponse::Ok) - VCSBase::VCSBaseOutputWindow::instance()->appendError(response.message); + response.message = sp_resp.exitMessage(executable, timeOut); return response; } @@ -1110,7 +1084,9 @@ bool CVSPlugin::vcsAdd(const QString &workingDir, const QString &rawFileName) { QStringList args; args << QLatin1String("add") << rawFileName; - const CVSResponse response = runCVS(workingDir, args, m_settings.timeOutMS(), true); + const CVSResponse response = + runCVS(workingDir, args, m_settings.timeOutMS(), + SshPasswordPrompt|ShowStdOutInLogWindow); return response.result == CVSResponse::Ok; } @@ -1118,7 +1094,9 @@ bool CVSPlugin::vcsDelete(const QString &workingDir, const QString &rawFileName) { QStringList args; args << QLatin1String("remove") << QLatin1String("-f") << rawFileName; - const CVSResponse response = runCVS(workingDir, args, m_settings.timeOutMS(), true); + const CVSResponse response = + runCVS(workingDir, args, m_settings.timeOutMS(), + SshPasswordPrompt|ShowStdOutInLogWindow); return response.result == CVSResponse::Ok; } diff --git a/src/plugins/cvs/cvsplugin.h b/src/plugins/cvs/cvsplugin.h index c95cb8369f9dc453be65a68ed1c858ddc992fbff..c1516595510c99d85fe51f0b4a27cf6aee260289 100644 --- a/src/plugins/cvs/cvsplugin.h +++ b/src/plugins/cvs/cvsplugin.h @@ -133,8 +133,7 @@ private: CVSResponse runCVS(const QString &workingDirectory, const QStringList &arguments, int timeOut, - bool showStdOutInOutputWindow, QTextCodec *outputCodec = 0, - bool mergeStderr = false); + unsigned flags, QTextCodec *outputCodec = 0); void annotate(const QString &workingDir, const QString &file, const QString &revision = QString(), int lineNumber= -1); diff --git a/src/plugins/debugger/debuggerdialogs.cpp b/src/plugins/debugger/debuggerdialogs.cpp index f4677bbcb58e6be25f1115e57dd36df8816a3236..ee7b4fae3dda67e496f292dfc318af5f5ee4e2fe 100644 --- a/src/plugins/debugger/debuggerdialogs.cpp +++ b/src/plugins/debugger/debuggerdialogs.cpp @@ -232,7 +232,7 @@ static QList<ProcData> unixProcessListPS() if (!psProcess.waitForStarted()) return rc; QByteArray output; - if (!Utils::SynchronousProcess::readDataFromProcess(psProcess, 30000, &output)) + if (!Utils::SynchronousProcess::readDataFromProcess(psProcess, 30000, &output, 0, false)) return rc; // Split "457 S+ /Users/foo.app" const QStringList lines = QString::fromLocal8Bit(output).split(QLatin1Char('\n')); diff --git a/src/plugins/git/clonewizardpage.cpp b/src/plugins/git/clonewizardpage.cpp index e2e3fdeb8b8bcd2eb0bf2efaced34f24c96b19a3..860a0c80633349c39bdadd17d262c6b08acbe2df 100644 --- a/src/plugins/git/clonewizardpage.cpp +++ b/src/plugins/git/clonewizardpage.cpp @@ -112,8 +112,9 @@ QSharedPointer<VCSBase::AbstractCheckoutJob> CloneWizardPage::createCheckoutJob( args << QLatin1String("clone") << repository() << checkoutDir; const QString binary = args.front(); args.pop_front(); - VCSBase::AbstractCheckoutJob *job = new VCSBase::ProcessCheckoutJob(binary, args, workingDirectory, - client->processEnvironment()); + VCSBase::AbstractCheckoutJob *job = + new VCSBase::ProcessCheckoutJob(binary, args, workingDirectory, + client->processEnvironment().toStringList()); return QSharedPointer<VCSBase::AbstractCheckoutJob>(job); } diff --git a/src/plugins/git/gitclient.cpp b/src/plugins/git/gitclient.cpp index d613a604c8ea6960dcbc3c40f186f964c23084d5..6bde23cb327035c84a9b92e7bb6e61cc21d3c81a 100644 --- a/src/plugins/git/gitclient.cpp +++ b/src/plugins/git/gitclient.cpp @@ -123,13 +123,6 @@ static inline QString msgParseFilesFailed() return GitClient::tr("Unable to parse the file output."); } -// Format a command for the status window -static QString formatCommand(const QString &binary, const QStringList &args) -{ - //: Executing: <executable> <arguments> - return GitClient::tr("Executing: %1 %2\n").arg(binary, args.join(QString(QLatin1Char(' ')))); -} - // ---------------- GitClient const char *GitClient::stashNamePrefix = "stash@{"; @@ -226,20 +219,20 @@ void GitClient::diff(const QString &workingDirectory, if (unstagedFileNames.empty() && stagedFileNames.empty()) { QStringList arguments(commonDiffArgs); arguments << diffArgs; - outputWindow()->appendCommand(formatCommand(binary, arguments)); + outputWindow()->appendCommand(workingDirectory, binary, arguments); command->addJob(arguments, m_settings.timeoutSeconds); } else { // Files diff. if (!unstagedFileNames.empty()) { QStringList arguments(commonDiffArgs); arguments << QLatin1String("--") << unstagedFileNames; - outputWindow()->appendCommand(formatCommand(binary, arguments)); + outputWindow()->appendCommand(workingDirectory, binary, arguments); command->addJob(arguments, m_settings.timeoutSeconds); } if (!stagedFileNames.empty()) { QStringList arguments(commonDiffArgs); arguments << QLatin1String("--cached") << diffArgs << QLatin1String("--") << stagedFileNames; - outputWindow()->appendCommand(formatCommand(binary, arguments)); + outputWindow()->appendCommand(workingDirectory, binary, arguments); command->addJob(arguments, m_settings.timeoutSeconds); } } @@ -1061,12 +1054,14 @@ GitCommand *GitClient::executeGit(const QString &workingDirectory, VCSBase::VCSBaseEditor* editor, bool outputToWindow, GitCommand::TerminationReportMode tm, - int editorLineNumber) + int editorLineNumber, + bool unixTerminalDisabled) { - outputWindow()->appendCommand(formatCommand(QLatin1String(Constants::GIT_BINARY), arguments)); + outputWindow()->appendCommand(workingDirectory, QLatin1String(Constants::GIT_BINARY), arguments); GitCommand *command = createCommand(workingDirectory, editor, outputToWindow, editorLineNumber); command->addJob(arguments, m_settings.timeoutSeconds); command->setTerminationReportMode(tm); + command->setUnixTerminalDisabled(unixTerminalDisabled); command->execute(); return command; } @@ -1083,14 +1078,14 @@ QStringList GitClient::binary() const #endif } -QStringList GitClient::processEnvironment() const +QProcessEnvironment GitClient::processEnvironment() const { - ProjectExplorer::Environment environment = ProjectExplorer::Environment::systemEnvironment(); + QProcessEnvironment environment = QProcessEnvironment::systemEnvironment(); if (m_settings.adoptPath) - environment.set(QLatin1String("PATH"), m_settings.path); - // git svn runs perl which barfs at non-C locales. - environment.set(QLatin1String("LANG"), QString(QLatin1Char('C'))); - return environment.toStringList(); + environment.insert(QLatin1String("PATH"), m_settings.path); + // Set up SSH and C locale (required by git using perl). + VCSBase::VCSBasePlugin::setProcessEnvironment(&environment); + return environment; } bool GitClient::synchronousGit(const QString &workingDirectory, @@ -1103,11 +1098,11 @@ bool GitClient::synchronousGit(const QString &workingDirectory, qDebug() << "synchronousGit" << workingDirectory << gitArguments; if (logCommandToWindow) - outputWindow()->appendCommand(formatCommand(m_binaryPath, gitArguments)); + outputWindow()->appendCommand(workingDirectory, m_binaryPath, gitArguments); QProcess process; process.setWorkingDirectory(workingDirectory); - process.setEnvironment(processEnvironment()); + process.setProcessEnvironment(processEnvironment()); QStringList args = binary(); // "cmd /c git" on Windows const QString executable = args.front(); @@ -1125,7 +1120,7 @@ bool GitClient::synchronousGit(const QString &workingDirectory, } if (!Utils::SynchronousProcess::readDataFromProcess(process, m_settings.timeoutSeconds * 1000, - outputText, errorText)) { + outputText, errorText, true)) { *errorText->append(GitCommand::msgTimeout(m_settings.timeoutSeconds).toLocal8Bit()); Utils::SynchronousProcess::stopProcess(process); return false; @@ -1506,7 +1501,10 @@ void GitClient::pull(const QString &workingDirectory, bool rebase) QStringList arguments(QLatin1String("pull")); if (rebase) arguments << QLatin1String("--rebase"); - GitCommand *cmd = executeGit(workingDirectory, arguments, 0, true, GitCommand::ReportStderr); + // Disable UNIX terminals to suppress SSH prompting. + GitCommand *cmd = executeGit(workingDirectory, arguments, 0, true, + GitCommand::ReportStderr, -1, + VCSBase::VCSBasePlugin::isSshPromptConfigured()); connectRepositoryChanged(workingDirectory, cmd); // Need to clean up if something goes wrong if (rebase) { @@ -1539,7 +1537,11 @@ void GitClient::subversionFetch(const QString &workingDirectory) { QStringList args; args << QLatin1String("svn") << QLatin1String("fetch"); - GitCommand *cmd = executeGit(workingDirectory, args, 0, true, GitCommand::ReportStderr); + // Disable UNIX terminals to suppress SSH prompting. + GitCommand *cmd = executeGit(workingDirectory, args, 0, true, GitCommand::ReportStderr, + -1, true); + // Enable SSH prompting + cmd->setUnixTerminalDisabled(VCSBase::VCSBasePlugin::isSshPromptConfigured()); connectRepositoryChanged(workingDirectory, cmd); } @@ -1563,7 +1565,10 @@ void GitClient::subversionLog(const QString &workingDirectory) void GitClient::push(const QString &workingDirectory) { - executeGit(workingDirectory, QStringList(QLatin1String("push")), 0, true, GitCommand::ReportStderr); + // Disable UNIX terminals to suppress SSH prompting. + executeGit(workingDirectory, QStringList(QLatin1String("push")), 0, + true, GitCommand::ReportStderr, + VCSBase::VCSBasePlugin::isSshPromptConfigured()); } QString GitClient::msgNoChangedFiles() diff --git a/src/plugins/git/gitclient.h b/src/plugins/git/gitclient.h index 26423fce1d5bdc43d16f7ffce7a4ddfa41693764..4b2882d0e6051c3fad8892f5aa26bb02dbfdffd0 100644 --- a/src/plugins/git/gitclient.h +++ b/src/plugins/git/gitclient.h @@ -42,6 +42,7 @@ QT_BEGIN_NAMESPACE class QErrorMessage; class QSignalMapper; class QDebug; +class QProcessEnvironment; QT_END_NAMESPACE namespace Core { @@ -202,7 +203,7 @@ public: void setSettings(const GitSettings &s); QStringList binary() const; // Executable + basic arguments - QStringList processEnvironment() const; + QProcessEnvironment processEnvironment() const; static QString msgNoChangedFiles(); @@ -233,7 +234,8 @@ private: VCSBase::VCSBaseEditor* editor = 0, bool outputToWindow = false, GitCommand::TerminationReportMode tm = GitCommand::NoReport, - int editorLineNumber = -1); + int editorLineNumber = -1, + bool unixTerminalDisabled = false); bool synchronousGit(const QString &workingDirectory, const QStringList &arguments, diff --git a/src/plugins/git/gitcommand.cpp b/src/plugins/git/gitcommand.cpp index 7483428eb91e7be18d3ce42b0baa81eed84e3a4c..b176e143436731765b035d494f93d72ca0aef60d 100644 --- a/src/plugins/git/gitcommand.cpp +++ b/src/plugins/git/gitcommand.cpp @@ -70,13 +70,14 @@ GitCommand::Job::Job(const QStringList &a, int t) : GitCommand::GitCommand(const QStringList &binary, const QString &workingDirectory, - const QStringList &environment, + const QProcessEnvironment&environment, const QVariant &cookie) : m_binaryPath(binary.front()), m_basicArguments(binary), m_workingDirectory(workingDirectory), m_environment(environment), m_cookie(cookie), + m_unixTerminalDisabled(false), m_reportTerminationMode(NoReport) { m_basicArguments.pop_front(); @@ -92,6 +93,16 @@ void GitCommand::setTerminationReportMode(TerminationReportMode m) m_reportTerminationMode = m; } +bool GitCommand::unixTerminalDisabled() const +{ + return m_unixTerminalDisabled; +} + +void GitCommand::setUnixTerminalDisabled(bool e) +{ + m_unixTerminalDisabled = e; +} + void GitCommand::addJob(const QStringList &arguments, int timeout) { m_jobs.push_back(Job(arguments, timeout)); @@ -121,12 +132,18 @@ QString GitCommand::msgTimeout(int seconds) void GitCommand::run() { if (Git::Constants::debug) - qDebug() << "GitCommand::run" << m_workingDirectory << m_jobs.size(); - QProcess process; + qDebug() << "GitCommand::run" << m_workingDirectory << m_jobs.size() + << "terminal_disabled" << m_unixTerminalDisabled; + + const unsigned processFlags = m_unixTerminalDisabled ? + unsigned(Utils::SynchronousProcess::UnixTerminalDisabled) : + unsigned(0); + const QSharedPointer<QProcess> process = + Utils::SynchronousProcess::createProcess(processFlags); if (!m_workingDirectory.isEmpty()) - process.setWorkingDirectory(m_workingDirectory); + process->setWorkingDirectory(m_workingDirectory); - process.setEnvironment(m_environment); + process->setProcessEnvironment(m_environment); QByteArray stdOut; QByteArray stdErr; @@ -139,25 +156,25 @@ void GitCommand::run() if (Git::Constants::debug) qDebug() << "GitCommand::run" << j << '/' << count << m_jobs.at(j).arguments; - process.start(m_binaryPath, m_basicArguments + m_jobs.at(j).arguments); - if(!process.waitForStarted()) { + process->start(m_binaryPath, m_basicArguments + m_jobs.at(j).arguments); + if(!process->waitForStarted()) { ok = false; - error += QString::fromLatin1("Error: \"%1\" could not be started: %2").arg(m_binaryPath, process.errorString()); + error += QString::fromLatin1("Error: \"%1\" could not be started: %2").arg(m_binaryPath, process->errorString()); break; } - process.closeWriteChannel(); + process->closeWriteChannel(); const int timeOutSeconds = m_jobs.at(j).timeout; - if (!Utils::SynchronousProcess::readDataFromProcess(process, timeOutSeconds * 1000, - &stdOut, &stdErr)) { - Utils::SynchronousProcess::stopProcess(process); + if (!Utils::SynchronousProcess::readDataFromProcess(*process, timeOutSeconds * 1000, + &stdOut, &stdErr, false)) { + Utils::SynchronousProcess::stopProcess(*process); ok = false; error += msgTimeout(timeOutSeconds); break; } error += QString::fromLocal8Bit(stdErr); - exitCode = process.exitCode(); + exitCode = process->exitCode(); switch (m_reportTerminationMode) { case NoReport: break; diff --git a/src/plugins/git/gitcommand.h b/src/plugins/git/gitcommand.h index c9cc058d0199076e019fe918cecd875ad59671f6..420e6f4dfaf893cf9625e16f0e1f2515304740ea 100644 --- a/src/plugins/git/gitcommand.h +++ b/src/plugins/git/gitcommand.h @@ -33,6 +33,7 @@ #include <QtCore/QObject> #include <QtCore/QStringList> #include <QtCore/QVariant> +#include <QtCore/QProcessEnvironment> QT_BEGIN_NAMESPACE class QProcess; @@ -55,7 +56,7 @@ public: explicit GitCommand(const QStringList &binary, const QString &workingDirectory, - const QStringList &environment, + const QProcessEnvironment &environment, const QVariant &cookie = QVariant()); @@ -70,6 +71,10 @@ public: TerminationReportMode reportTerminationMode() const; void setTerminationReportMode(TerminationReportMode m); + // Disable Terminal on UNIX (see VCS SSH handling). + bool unixTerminalDisabled() const; + void setUnixTerminalDisabled(bool); + static QString msgTimeout(int seconds); void setCookie(const QVariant &cookie); @@ -95,8 +100,9 @@ private: const QString m_binaryPath; QStringList m_basicArguments; const QString m_workingDirectory; - const QStringList m_environment; + const QProcessEnvironment m_environment; QVariant m_cookie; + bool m_unixTerminalDisabled; QList<Job> m_jobs; TerminationReportMode m_reportTerminationMode; diff --git a/src/plugins/mercurial/mercurialclient.cpp b/src/plugins/mercurial/mercurialclient.cpp index ff78f0dc4bb101d2e5bf818128172c066618e953..a7bf38b09fb522f5ecb24b1d414e96c2dec982fc 100644 --- a/src/plugins/mercurial/mercurialclient.cpp +++ b/src/plugins/mercurial/mercurialclient.cpp @@ -136,7 +136,7 @@ bool MercurialClient::executeHgSynchronously(const QString &workingDir, const QStringList arguments = settings.standardArguments() + args; VCSBase::VCSBaseOutputWindow *outputWindow = VCSBase::VCSBaseOutputWindow::instance(); - outputWindow->appendCommand(MercurialJobRunner::msgExecute(binary, arguments)); + outputWindow->appendCommand(workingDir, binary, args); hgProcess.start(binary, arguments); @@ -149,7 +149,7 @@ bool MercurialClient::executeHgSynchronously(const QString &workingDir, QByteArray stdErr; if (!Utils::SynchronousProcess::readDataFromProcess(hgProcess, settings.timeoutMilliSeconds(), - output, &stdErr)) { + output, &stdErr, true)) { Utils::SynchronousProcess::stopProcess(hgProcess); outputWindow->appendError(MercurialJobRunner::msgTimeout(settings.timeoutSeconds())); return false; @@ -457,6 +457,8 @@ void MercurialClient::pull(const QString &repositoryRoot, const QString &reposit if (!repository.isEmpty()) args.append(repository); QSharedPointer<HgTask> job(new HgTask(repositoryRoot, args, false, QVariant(repositoryRoot))); + // Suppress SSH prompting + job->setUnixTerminalDisabled(VCSBase::VCSBasePlugin::isSshPromptConfigured()); connect(job.data(), SIGNAL(succeeded(QVariant)), this, SIGNAL(changed(QVariant)), Qt::QueuedConnection); enqueueJob(job); } @@ -468,6 +470,8 @@ void MercurialClient::push(const QString &repositoryRoot, const QString &reposit args.append(repository); QSharedPointer<HgTask> job(new HgTask(repositoryRoot, args, false)); + // Suppress SSH prompting + job->setUnixTerminalDisabled(VCSBase::VCSBasePlugin::isSshPromptConfigured()); enqueueJob(job); } @@ -491,6 +495,9 @@ void MercurialClient::incoming(const QString &repositoryRoot, const QString &rep true, "incoming", id); QSharedPointer<HgTask> job(new HgTask(repositoryRoot, args, editor)); + // Suppress SSH prompting. + if (!repository.isEmpty() && VCSBase::VCSBasePlugin::isSshPromptConfigured()) + job->setUnixTerminalDisabled(true); enqueueJob(job); } @@ -506,6 +513,8 @@ void MercurialClient::outgoing(const QString &repositoryRoot) "outgoing", repositoryRoot); QSharedPointer<HgTask> job(new HgTask(repositoryRoot, args, editor)); + // Suppress SSH prompting + job->setUnixTerminalDisabled(VCSBase::VCSBasePlugin::isSshPromptConfigured()); enqueueJob(job); } @@ -532,6 +541,8 @@ void MercurialClient::update(const QString &repositoryRoot, const QString &revis args << revision; QSharedPointer<HgTask> job(new HgTask(repositoryRoot, args, false, QVariant(repositoryRoot))); + // Suppress SSH prompting + job->setUnixTerminalDisabled(VCSBase::VCSBasePlugin::isSshPromptConfigured()); connect(job.data(), SIGNAL(succeeded(QVariant)), this, SIGNAL(changed(QVariant)), Qt::QueuedConnection); enqueueJob(job); } diff --git a/src/plugins/mercurial/mercurialjobrunner.cpp b/src/plugins/mercurial/mercurialjobrunner.cpp index 54f343c122ddb04a90bd7e453efb0a5dbe7e80aa..0c0be01ed38c08db1b32cb20fba8f0214ad08e1e 100644 --- a/src/plugins/mercurial/mercurialjobrunner.cpp +++ b/src/plugins/mercurial/mercurialjobrunner.cpp @@ -34,6 +34,7 @@ #include <vcsbase/vcsbaseoutputwindow.h> #include <vcsbase/vcsbaseeditor.h> +#include <vcsbase/vcsbaseplugin.h> #include <utils/synchronousprocess.h> #include <QtCore/QProcess> @@ -52,8 +53,8 @@ HgTask::HgTask(const QString &repositoryRoot, arguments(arguments), emitRaw(emitRaw), m_cookie(cookie), - editor(0) - + editor(0), + m_unixTerminalDisabled(false) { } @@ -65,7 +66,8 @@ HgTask::HgTask(const QString &repositoryRoot, arguments(arguments), emitRaw(false), m_cookie(cookie), - editor(editor) + editor(editor), + m_unixTerminalDisabled(false) { } @@ -147,11 +149,6 @@ void MercurialJobRunner::run() } } -QString MercurialJobRunner::msgExecute(const QString &binary, const QStringList &args) -{ - return tr("Executing: %1 %2\n").arg(binary, args.join(QString(QLatin1Char(' ')))); -} - QString MercurialJobRunner::msgStartFailed(const QString &binary, const QString &why) { return tr("Unable to start mercurial process '%1': %2").arg(binary, why); @@ -168,7 +165,7 @@ QString MercurialJobRunner::msgTimeout(int timeoutSeconds) void MercurialJobRunner::setProcessEnvironment(QProcess &p) { QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); - env.insert(QLatin1String("LANG"), QString(QLatin1Char('C'))); + VCSBase::VCSBasePlugin::setProcessEnvironment(&env); p.setProcessEnvironment(env); } @@ -196,35 +193,41 @@ void MercurialJobRunner::task(const QSharedPointer<HgTask> &job) } const QStringList args = standardArguments + taskData->args(); - emit commandStarted(msgExecute(binary, args)); + emit commandStarted(VCSBase::VCSBaseOutputWindow::msgExecutionLogEntry(taskData->repositoryRoot(), binary, args)); //infom the user of what we are going to try and perform if (Constants::debug) - qDebug() << Q_FUNC_INFO << "Repository root is " << taskData->repositoryRoot(); + qDebug() << Q_FUNC_INFO << "Repository root is " + << taskData->repositoryRoot() << " terminal_disabled" + << taskData->unixTerminalDisabled(); + + const unsigned processFlags = taskData->unixTerminalDisabled() ? + unsigned(Utils::SynchronousProcess::UnixTerminalDisabled) : + unsigned(0); - QProcess hgProcess; - hgProcess.setWorkingDirectory(taskData->repositoryRoot()); - MercurialJobRunner::setProcessEnvironment(hgProcess); + QSharedPointer<QProcess> hgProcess = Utils::SynchronousProcess::createProcess(processFlags); + hgProcess->setWorkingDirectory(taskData->repositoryRoot()); + MercurialJobRunner::setProcessEnvironment(*hgProcess); - hgProcess.start(binary, args); + hgProcess->start(binary, args); - if (!hgProcess.waitForStarted()) { - emit error(msgStartFailed(binary, hgProcess.errorString())); + if (!hgProcess->waitForStarted()) { + emit error(msgStartFailed(binary, hgProcess->errorString())); return; } - hgProcess.closeWriteChannel(); + hgProcess->closeWriteChannel(); QByteArray stdOutput; QByteArray stdErr; - if (!Utils::SynchronousProcess::readDataFromProcess(hgProcess, m_timeoutMS, &stdOutput, &stdErr)) { - Utils::SynchronousProcess::stopProcess(hgProcess); + if (!Utils::SynchronousProcess::readDataFromProcess(*hgProcess, m_timeoutMS, &stdOutput, &stdErr, false)) { + Utils::SynchronousProcess::stopProcess(*hgProcess); emit error(msgTimeout(m_timeoutMS / 1000)); return; } - if (hgProcess.exitStatus() == QProcess::NormalExit && hgProcess.exitCode() == 0) { + if (hgProcess->exitStatus() == QProcess::NormalExit && hgProcess->exitCode() == 0) { /* * sometimes success means output is actually on error channel (stderr) * e.g. "hg revert" outputs "no changes needed to 'file'" on stderr if file has not changed @@ -238,7 +241,7 @@ void MercurialJobRunner::task(const QSharedPointer<HgTask> &job) emit error(QString::fromLocal8Bit(stdErr)); } - hgProcess.close(); + hgProcess->close(); //the signal connection is to last only for the duration of a job/task. next time a new //output signal connection must be made disconnect(this, SIGNAL(output(QByteArray)), 0, 0); diff --git a/src/plugins/mercurial/mercurialjobrunner.h b/src/plugins/mercurial/mercurialjobrunner.h index df6f1f79d632b214cdc74e0d9694f6131eff4410..a7bc219c16ef02bad0eb073487bafc43f4499e44 100644 --- a/src/plugins/mercurial/mercurialjobrunner.h +++ b/src/plugins/mercurial/mercurialjobrunner.h @@ -69,6 +69,10 @@ public: QStringList args() { return arguments; } QString repositoryRoot() { return m_repositoryRoot; } + // Disable terminal to suppress SSH prompting. + bool unixTerminalDisabled() const { return m_unixTerminalDisabled; } + void setUnixTerminalDisabled(bool v) { m_unixTerminalDisabled = v; } + signals: void succeeded(const QVariant &cookie); // Use a queued connection void rawData(const QByteArray &data); @@ -82,6 +86,7 @@ private: const bool emitRaw; const QVariant m_cookie; VCSBase::VCSBaseEditor *editor; + bool m_unixTerminalDisabled; }; /* A job queue running in a separate thread, executing commands @@ -95,7 +100,6 @@ public: void enqueueJob(const QSharedPointer<HgTask> &job); void restart(); - static QString msgExecute(const QString &binary, const QStringList &args); static QString msgStartFailed(const QString &binary, const QString &why); static QString msgTimeout(int timeoutSeconds); diff --git a/src/plugins/perforce/perforceplugin.cpp b/src/plugins/perforce/perforceplugin.cpp index 4a44dc43a5021ac6b8fadddc0f50efdf8dc91bc9..49ea0afd3814e2a3df103e7d8827c363c72340a8 100644 --- a/src/plugins/perforce/perforceplugin.cpp +++ b/src/plugins/perforce/perforceplugin.cpp @@ -947,15 +947,6 @@ bool PerforcePlugin::vcsMove(const QString &workingDir, const QString &from, con return !moveResult.error; } -static QString formatCommand(const QString &cmd, const QStringList &args) -{ - const QChar blank = QLatin1Char(' '); - QString command = cmd; - command += blank; - command += args.join(QString(blank)); - return PerforcePlugin::tr("Executing: %1\n").arg(command); -} - // Write extra args to temporary file QSharedPointer<QTemporaryFile> PerforcePlugin::createTemporaryArgumentFile(const QStringList &extraArgs) const @@ -1041,6 +1032,7 @@ PerforceResponse PerforcePlugin::synchronousProcess(const QString &workingDir, } if (Perforce::Constants::debug) qDebug() << "PerforcePlugin::run syncp actual args [" << process.workingDirectory() << ']' << args; + process.setTimeOutMessageBoxEnabled(true); const Utils::SynchronousProcessResponse sp_resp = process.run(m_settings.p4Command(), args); if (Perforce::Constants::debug) qDebug() << sp_resp; @@ -1111,7 +1103,7 @@ PerforceResponse PerforcePlugin::fullySynchronousProcess(const QString &workingD QByteArray stdOut; QByteArray stdErr; const int timeOut = (flags & LongTimeOut) ? m_settings.longTimeOutMS() : m_settings.timeOutMS(); - if (!Utils::SynchronousProcess::readDataFromProcess(process, timeOut, &stdOut, &stdErr)) { + if (!Utils::SynchronousProcess::readDataFromProcess(process, timeOut, &stdOut, &stdErr, true)) { Utils::SynchronousProcess::stopProcess(process); response.error = true; response.message = msgTimeout(timeOut); @@ -1164,7 +1156,7 @@ PerforceResponse PerforcePlugin::runP4Cmd(const QString &workingDir, actualArgs.append(args); if (flags & CommandToWindow) - outputWindow->appendCommand(formatCommand(m_settings.p4Command(), actualArgs)); + outputWindow->appendCommand(workingDir, m_settings.p4Command(), actualArgs); if (flags & ShowBusyCursor) QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); diff --git a/src/plugins/subversion/subversionplugin.cpp b/src/plugins/subversion/subversionplugin.cpp index ff299b1912903f0c62e29bee9f7db3cad34d8dc7..7ae56c8d4f58372543ce2f4de3242efcfa0da54f 100644 --- a/src/plugins/subversion/subversionplugin.cpp +++ b/src/plugins/subversion/subversionplugin.cpp @@ -105,6 +105,8 @@ static const char * const CMD_ID_DESCRIBE = "Subversion.Describe"; static const char *nonInteractiveOptionC = "--non-interactive"; + + static const VCSBase::VCSBaseEditorParameters editorParameters[] = { { VCSBase::RegularCommandOutput, @@ -548,7 +550,8 @@ void SubversionPlugin::svnDiff(const QString &workingDir, const QStringList &fil QStringList args(QLatin1String("diff")); args << files; - const SubversionResponse response = runSvn(workingDir, args, m_settings.timeOutMS(), false, codec); + const SubversionResponse response = + runSvn(workingDir, args, m_settings.timeOutMS(), 0); if (response.error) return; @@ -635,7 +638,9 @@ void SubversionPlugin::revertAll() // NoteL: Svn "revert ." doesn not work. QStringList args; args << QLatin1String("revert") << QLatin1String("--recursive") << state.topLevel(); - const SubversionResponse revertResponse = runSvn(state.topLevel(), args, m_settings.timeOutMS(), true); + const SubversionResponse revertResponse = + runSvn(state.topLevel(), args, m_settings.timeOutMS(), + SshPasswordPrompt|ShowStdOutInLogWindow); if (revertResponse.error) { QMessageBox::warning(0, title, tr("Revert failed: %1").arg(revertResponse.message), QMessageBox::Ok); } else { @@ -651,7 +656,8 @@ void SubversionPlugin::revertCurrentFile() QStringList args(QLatin1String("diff")); args.push_back(state.relativeCurrentFile()); - const SubversionResponse diffResponse = runSvn(state.currentFileTopLevel(), args, m_settings.timeOutMS(), false); + const SubversionResponse diffResponse = + runSvn(state.currentFileTopLevel(), args, m_settings.timeOutMS(), 0); if (diffResponse.error) return; @@ -668,7 +674,10 @@ void SubversionPlugin::revertCurrentFile() args.clear(); args << QLatin1String("revert") << state.relativeCurrentFile(); - const SubversionResponse revertResponse = runSvn(state.currentFileTopLevel(), args, m_settings.timeOutMS(), true); + const SubversionResponse revertResponse = + runSvn(state.currentFileTopLevel(), args, m_settings.timeOutMS(), + SshPasswordPrompt|ShowStdOutInLogWindow); + if (!revertResponse.error) { subVersionControl()->emitFilesChanged(QStringList(state.currentFile())); } @@ -724,7 +733,8 @@ void SubversionPlugin::startCommit(const QString &workingDir, const QStringList QStringList args(QLatin1String("status")); args += files; - const SubversionResponse response = runSvn(workingDir, args, m_settings.timeOutMS(), false); + const SubversionResponse response = + runSvn(workingDir, args, m_settings.timeOutMS(), 0); if (response.error) return; @@ -765,7 +775,9 @@ bool SubversionPlugin::commit(const QString &messageFile, QStringList args = QStringList(QLatin1String("commit")); args << QLatin1String(nonInteractiveOptionC) << QLatin1String("--file") << messageFile; args.append(subVersionFileList); - const SubversionResponse response = runSvn(m_commitRepository, args, m_settings.longTimeOutMS(), true); + const SubversionResponse response = + runSvn(m_commitRepository, args, m_settings.longTimeOutMS(), + SshPasswordPrompt|ShowStdOutInLogWindow); return !response.error ; } @@ -820,7 +832,8 @@ void SubversionPlugin::svnStatus(const QString &workingDir, const QStringList &r args.append(relativePaths); VCSBase::VCSBaseOutputWindow *outwin = VCSBase::VCSBaseOutputWindow::instance(); outwin->setRepository(workingDir); - runSvn(workingDir, args, m_settings.timeOutMS(), true); + runSvn(workingDir, args, m_settings.timeOutMS(), + ShowStdOutInLogWindow); outwin->clearRepository(); } @@ -836,7 +849,9 @@ void SubversionPlugin::filelog(const QString &workingDir, foreach(const QString &file, files) args.append(QDir::toNativeSeparators(file)); - const SubversionResponse response = runSvn(workingDir, args, m_settings.timeOutMS(), false, codec); + const SubversionResponse response = + runSvn(workingDir, args, m_settings.timeOutMS(), + SshPasswordPrompt, codec); if (response.error) return; @@ -870,7 +885,9 @@ void SubversionPlugin::svnUpdate(const QString &workingDir, const QStringList &r args.push_back(QLatin1String(nonInteractiveOptionC)); if (!relativePaths.isEmpty()) args.append(relativePaths); - const SubversionResponse response = runSvn(workingDir, args, m_settings.longTimeOutMS(), true); + const SubversionResponse response = + runSvn(workingDir, args, m_settings.longTimeOutMS(), + SshPasswordPrompt|ShowStdOutInLogWindow); if (!response.error) subVersionControl()->emitRepositoryChanged(workingDir); } @@ -904,7 +921,9 @@ void SubversionPlugin::vcsAnnotate(const QString &workingDir, const QString &fil args.push_back(QLatin1String("-v")); args.append(QDir::toNativeSeparators(file)); - const SubversionResponse response = runSvn(workingDir, args, m_settings.timeOutMS(), false, codec); + const SubversionResponse response = + runSvn(workingDir, args, m_settings.timeOutMS(), + SshPasswordPrompt, codec); if (response.error) return; @@ -956,7 +975,8 @@ void SubversionPlugin::describe(const QString &source, const QString &changeNr) QStringList args(QLatin1String("log")); args.push_back(QLatin1String("-r")); args.push_back(changeNr); - const SubversionResponse logResponse = runSvn(topLevel, args, m_settings.timeOutMS(), false); + const SubversionResponse logResponse = + runSvn(topLevel, args, m_settings.timeOutMS(), SshPasswordPrompt); if (logResponse.error) return; description = logResponse.stdOut; @@ -970,7 +990,9 @@ void SubversionPlugin::describe(const QString &source, const QString &changeNr) args.push_back(diffArg); QTextCodec *codec = VCSBase::VCSBaseEditor::getCodec(source); - const SubversionResponse response = runSvn(topLevel, args, m_settings.timeOutMS(), false, codec); + const SubversionResponse response = + runSvn(topLevel, args, m_settings.timeOutMS(), + SshPasswordPrompt, codec); if (response.error) return; description += response.stdOut; @@ -1013,32 +1035,10 @@ void SubversionPlugin::submitCurrentLog() << Core::EditorManager::instance()->currentEditor()); } -static inline QString processStdErr(QProcess &proc) -{ - return QString::fromLocal8Bit(proc.readAllStandardError()).remove(QLatin1Char('\r')); -} - -static inline QString processStdOut(QProcess &proc, QTextCodec *outputCodec = 0) -{ - const QByteArray stdOutData = proc.readAllStandardOutput(); - QString stdOut = outputCodec ? outputCodec->toUnicode(stdOutData) : QString::fromLocal8Bit(stdOutData); - return stdOut.remove(QLatin1Char('\r')); -} - -// Format log entry for command -static inline QString msgExecutionLogEntry(const QString &workingDir, const QString &executable, const QStringList &arguments) -{ - const QString argsS = SubversionSettings::formatArguments(arguments); - if (workingDir.isEmpty()) - return SubversionPlugin::tr("Executing: %1 %2\n").arg(executable, argsS); - return SubversionPlugin::tr("Executing in %1: %2 %3\n"). - arg(QDir::toNativeSeparators(workingDir), executable, argsS); -} - SubversionResponse SubversionPlugin::runSvn(const QString &workingDir, const QStringList &arguments, int timeOut, - bool showStdOutInOutputWindow, + unsigned flags, QTextCodec *outputCodec) { const QString executable = m_settings.svnCommand; @@ -1048,60 +1048,17 @@ SubversionResponse SubversionPlugin::runSvn(const QString &workingDir, response.message =tr("No subversion executable specified!"); return response; } - const QStringList allArgs = m_settings.addOptions(arguments); - VCSBase::VCSBaseOutputWindow *outputWindow = VCSBase::VCSBaseOutputWindow::instance(); - // Hide passwords, etc in the log window - //: Executing: <executable> <arguments> - const QString outputText = msgExecutionLogEntry(workingDir, executable, allArgs); - outputWindow->appendCommand(outputText); + const Utils::SynchronousProcessResponse sp_resp = + VCSBase::VCSBasePlugin::runVCS(workingDir, executable, + m_settings.addOptions(arguments), + timeOut, flags, outputCodec); - if (Subversion::Constants::debug) - qDebug() << "runSvn" << timeOut << outputText; - - // Run, connect stderr to the output window - Utils::SynchronousProcess process; - if (!workingDir.isEmpty()) - process.setWorkingDirectory(workingDir); - QProcessEnvironment env = process.processEnvironment(); - env.insert(QLatin1String("LANG"), QString(QLatin1Char('C'))); - process.setProcessEnvironment(env); - process.setTimeout(timeOut); - process.setStdOutCodec(outputCodec); - - process.setStdErrBufferedSignalsEnabled(true); - connect(&process, SIGNAL(stdErrBuffered(QString,bool)), outputWindow, SLOT(append(QString))); - - // connect stdout to the output window if desired - if (showStdOutInOutputWindow) { - process.setStdOutBufferedSignalsEnabled(true); - connect(&process, SIGNAL(stdOutBuffered(QString,bool)), outputWindow, SLOT(append(QString))); - } - - const Utils::SynchronousProcessResponse sp_resp = process.run(executable, allArgs); - response.error = true; + response.error = sp_resp.result != Utils::SynchronousProcessResponse::Finished; + if (response.error) + response.message = sp_resp.exitMessage(executable, timeOut); response.stdErr = sp_resp.stdErr; response.stdOut = sp_resp.stdOut; - switch (sp_resp.result) { - case Utils::SynchronousProcessResponse::Finished: - response.error = false; - break; - case Utils::SynchronousProcessResponse::FinishedError: - response.message = tr("The process terminated with exit code %1.").arg(sp_resp.exitCode); - break; - case Utils::SynchronousProcessResponse::TerminatedAbnormally: - response.message = tr("The process terminated abnormally."); - break; - case Utils::SynchronousProcessResponse::StartFailed: - response.message = tr("Could not start subversion '%1'. Please check your settings in the preferences.").arg(executable); - break; - case Utils::SynchronousProcessResponse::Hang: - response.message = tr("Subversion did not respond within timeout limit (%1 ms).").arg(timeOut); - break; - } - if (response.error) - outputWindow->appendError(response.message); - return response; } @@ -1167,7 +1124,9 @@ bool SubversionPlugin::vcsAdd15(const QString &workingDir, const QString &rawFil const QString file = QDir::toNativeSeparators(rawFileName); QStringList args; args << QLatin1String("add") << QLatin1String("--parents") << file; - const SubversionResponse response = runSvn(workingDir, args, m_settings.timeOutMS(), true); + const SubversionResponse response = + runSvn(workingDir, args, m_settings.timeOutMS(), + SshPasswordPrompt|ShowStdOutInLogWindow); return !response.error; } @@ -1188,7 +1147,9 @@ bool SubversionPlugin::vcsAdd14(const QString &workingDir, const QString &rawFil if (!checkSVNSubDir(QDir(path))) { QStringList addDirArgs; addDirArgs << QLatin1String("add") << QLatin1String("--non-recursive") << QDir::toNativeSeparators(path); - const SubversionResponse addDirResponse = runSvn(workingDir, addDirArgs, m_settings.timeOutMS(), true); + const SubversionResponse addDirResponse = + runSvn(workingDir, addDirArgs, m_settings.timeOutMS(), + SshPasswordPrompt|ShowStdOutInLogWindow); if (addDirResponse.error) return false; } @@ -1197,7 +1158,9 @@ bool SubversionPlugin::vcsAdd14(const QString &workingDir, const QString &rawFil // Add file QStringList args; args << QLatin1String("add") << QDir::toNativeSeparators(rawFileName); - const SubversionResponse response = runSvn(workingDir, args, m_settings.timeOutMS(), true); + const SubversionResponse response = + runSvn(workingDir, args, m_settings.timeOutMS(), + SshPasswordPrompt|ShowStdOutInLogWindow); return !response.error; } @@ -1208,7 +1171,9 @@ bool SubversionPlugin::vcsDelete(const QString &workingDir, const QString &rawFi QStringList args(QLatin1String("delete")); args.push_back(file); - const SubversionResponse response = runSvn(workingDir, args, m_settings.timeOutMS(), true); + const SubversionResponse response = + runSvn(workingDir, args, m_settings.timeOutMS(), + SshPasswordPrompt|ShowStdOutInLogWindow); return !response.error; } @@ -1217,7 +1182,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(), true); + const SubversionResponse response = + runSvn(workingDir, args, m_settings.timeOutMS(), + SshPasswordPrompt|ShowStdOutInLogWindow); qDebug() << response.stdOut << "\n"<<response.stdErr; return !response.error; } diff --git a/src/plugins/subversion/subversionplugin.h b/src/plugins/subversion/subversionplugin.h index 278cb2d30336041a64d82d34a60fcb337594c430..31ef436ebf29a5bf96f4dc7e2e1d4946b1f5452c 100644 --- a/src/plugins/subversion/subversionplugin.h +++ b/src/plugins/subversion/subversionplugin.h @@ -138,7 +138,7 @@ private: QTextCodec *codec); SubversionResponse runSvn(const QString &workingDir, const QStringList &arguments, int timeOut, - bool showStdOutInOutputWindow, QTextCodec *outputCodec = 0); + unsigned flags, QTextCodec *outputCodec = 0); void filelog(const QString &workingDir, const QStringList &file = QStringList(), bool enableAnnotationContextMenu = false); diff --git a/src/plugins/subversion/subversionsettings.cpp b/src/plugins/subversion/subversionsettings.cpp index 3a38c7a618d5ecbed8f3a3f18f9cb007559a18fc..91796f08cd67e4558ce74660262958e23e664029 100644 --- a/src/plugins/subversion/subversionsettings.cpp +++ b/src/plugins/subversion/subversionsettings.cpp @@ -30,20 +30,19 @@ #include "subversionsettings.h" #include <QtCore/QSettings> -#include <QtCore/QTextStream> -static const char *groupC = "Subversion"; -static const char *commandKeyC = "Command"; -static const char *userKeyC = "User"; -static const char *passwordKeyC = "Password"; -static const char *authenticationKeyC = "Authentication"; +static const char groupC[] = "Subversion"; +static const char commandKeyC[] = "Command"; +static const char userKeyC[] = "User"; +static const char passwordKeyC[] = "Password"; +static const char authenticationKeyC[] = "Authentication"; -static const char *userNameOptionC = "--username"; -static const char *passwordOptionC = "--password"; -static const char *promptToSubmitKeyC = "PromptForSubmit"; -static const char *timeOutKeyC = "TimeOut"; -static const char *spaceIgnorantAnnotationKeyC = "SpaceIgnorantAnnotation"; -static const char *logCountKeyC = "LogCount"; +static const char userNameOptionC[] = "--username"; +static const char passwordOptionC[] = "--password"; +static const char promptToSubmitKeyC[] = "PromptForSubmit"; +static const char timeOutKeyC[] = "TimeOut"; +static const char spaceIgnorantAnnotationKeyC[] = "SpaceIgnorantAnnotation"; +static const char logCountKeyC[] = "LogCount"; enum { defaultTimeOutS = 30, defaultLogCount = 1000 }; @@ -128,23 +127,3 @@ QStringList SubversionSettings::addOptions(const QStringList &args) const rc.append(args); return rc; } - -// Format arguments for log windows hiding passwords, etc. -QString SubversionSettings::formatArguments(const QStringList &args) -{ - QString rc; - QTextStream str(&rc); - const int size = args.size(); - // Skip authentication options - for (int i = 0; i < size; i++) { - const QString &arg = args.at(i); - if (i) - str << ' '; - str << arg; - if (arg == QLatin1String(userNameOptionC) || arg == QLatin1String(passwordOptionC)) { - str << " ********"; - i++; - } - } - return rc; -} diff --git a/src/plugins/subversion/subversionsettings.h b/src/plugins/subversion/subversionsettings.h index 1fca010d06f6596550cc9a050bef1cbe5ce22d2a..9eaa8e0c9e715be9fc9f9679c17e21353790d90a 100644 --- a/src/plugins/subversion/subversionsettings.h +++ b/src/plugins/subversion/subversionsettings.h @@ -53,8 +53,6 @@ struct SubversionSettings // Add authentication and (maybe future) options to the // command line QStringList addOptions(const QStringList &args) const; - // Format arguments for log windows hiding passwords, etc. - static QString formatArguments(const QStringList &args); bool equals(const SubversionSettings &s) const; diff --git a/src/plugins/vcsbase/checkoutjobs.cpp b/src/plugins/vcsbase/checkoutjobs.cpp index 3d18fed83e2a1510a431dc47dadd6eae30b6d669..e707595637b764e7c3bdde0e899a52ccd49f7050 100644 --- a/src/plugins/vcsbase/checkoutjobs.cpp +++ b/src/plugins/vcsbase/checkoutjobs.cpp @@ -29,6 +29,8 @@ #include "checkoutjobs.h" +#include <vcsbaseplugin.h> + #include <QtCore/QDebug> #include <utils/synchronousprocess.h> @@ -46,22 +48,35 @@ struct ProcessCheckoutJobPrivate { const QString &workingDirectory, const QStringList &env); - QProcess process; + QSharedPointer<QProcess> process; const QString binary; const QStringList args; }; +// Use a terminal-less process to suppress SSH prompts. +static inline QSharedPointer<QProcess> createProcess() +{ + unsigned flags = 0; + if (VCSBasePlugin::isSshPromptConfigured()) + flags = Utils::SynchronousProcess::UnixTerminalDisabled; + return Utils::SynchronousProcess::createProcess(flags); +} + ProcessCheckoutJobPrivate::ProcessCheckoutJobPrivate(const QString &b, const QStringList &a, const QString &workingDirectory, const QStringList &env) : + process(createProcess()), binary(b), args(a) -{ +{ if (!workingDirectory.isEmpty()) - process.setWorkingDirectory(workingDirectory); + process->setWorkingDirectory(workingDirectory); if (!env.empty()) - process.setEnvironment(env); + process->setEnvironment(env); + QProcessEnvironment processEnv = process->processEnvironment(); + VCSBasePlugin::setProcessEnvironment(&processEnv); + process->setProcessEnvironment(processEnv); } ProcessCheckoutJob::ProcessCheckoutJob(const QString &binary, @@ -74,11 +89,11 @@ ProcessCheckoutJob::ProcessCheckoutJob(const QString &binary, { if (debug) qDebug() << "ProcessCheckoutJob" << binary << args << workingDirectory; - connect(&d->process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(slotError(QProcess::ProcessError))); - connect(&d->process, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(slotFinished(int,QProcess::ExitStatus))); - connect(&d->process, SIGNAL(readyReadStandardOutput()), this, SLOT(slotOutput())); - d->process.setProcessChannelMode(QProcess::MergedChannels); - d->process.closeWriteChannel(); + connect(d->process.data(), SIGNAL(error(QProcess::ProcessError)), this, SLOT(slotError(QProcess::ProcessError))); + connect(d->process.data(), SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(slotFinished(int,QProcess::ExitStatus))); + connect(d->process.data(), SIGNAL(readyReadStandardOutput()), this, SLOT(slotOutput())); + d->process->setProcessChannelMode(QProcess::MergedChannels); + d->process->closeWriteChannel(); } ProcessCheckoutJob::~ProcessCheckoutJob() @@ -88,7 +103,7 @@ ProcessCheckoutJob::~ProcessCheckoutJob() void ProcessCheckoutJob::slotOutput() { - const QByteArray data = d->process.readAllStandardOutput(); + const QByteArray data = d->process->readAllStandardOutput(); const QString s = QString::fromLocal8Bit(data, data.endsWith('\n') ? data.size() - 1: data.size()); if (debug) qDebug() << s; @@ -99,10 +114,10 @@ void ProcessCheckoutJob::slotError(QProcess::ProcessError error) { switch (error) { case QProcess::FailedToStart: - emit failed(tr("Unable to start %1: %2").arg(d->binary, d->process.errorString())); + emit failed(tr("Unable to start %1: %2").arg(d->binary, d->process->errorString())); break; default: - emit failed(d->process.errorString()); + emit failed(d->process->errorString()); break; } } @@ -129,7 +144,7 @@ void ProcessCheckoutJob::slotFinished (int exitCode, QProcess::ExitStatus exitSt void ProcessCheckoutJob::start() { - d->process.start(d->binary, d->args); + d->process->start(d->binary, d->args); } void ProcessCheckoutJob::cancel() @@ -138,7 +153,7 @@ void ProcessCheckoutJob::cancel() qDebug() << "ProcessCheckoutJob::start"; emit output(tr("Stopping...")); - Utils::SynchronousProcess::stopProcess(d->process); + Utils::SynchronousProcess::stopProcess(*d->process); } } // namespace VCSBase diff --git a/src/plugins/vcsbase/commonsettingspage.cpp b/src/plugins/vcsbase/commonsettingspage.cpp index d9d2ffb1511e709fc05776aaad9f11040b2af009..c382170b3148308048abe8271e753978d9aa2474 100644 --- a/src/plugins/vcsbase/commonsettingspage.cpp +++ b/src/plugins/vcsbase/commonsettingspage.cpp @@ -53,6 +53,7 @@ CommonSettingsWidget::CommonSettingsWidget(QWidget *parent) : m_ui->submitMessageCheckScriptChooser->setExpectedKind(Utils::PathChooser::Command); m_ui->nickNameFieldsFileChooser->setExpectedKind(Utils::PathChooser::File); m_ui->nickNameMailMapChooser->setExpectedKind(Utils::PathChooser::File); + m_ui->sshPromptChooser->setExpectedKind(Utils::PathChooser::Command); } CommonSettingsWidget::~CommonSettingsWidget() @@ -68,6 +69,7 @@ CommonVcsSettings CommonSettingsWidget::settings() const rc.submitMessageCheckScript = m_ui->submitMessageCheckScriptChooser->path(); rc.lineWrap= m_ui->lineWrapCheckBox->isChecked(); rc.lineWrapWidth = m_ui->lineWrapSpinBox->value(); + rc.sshPasswordPrompt = m_ui->sshPromptChooser->path(); return rc; } @@ -78,6 +80,7 @@ void CommonSettingsWidget::setSettings(const CommonVcsSettings &s) m_ui->submitMessageCheckScriptChooser->setPath(s.submitMessageCheckScript); m_ui->lineWrapCheckBox->setChecked(s.lineWrap); m_ui->lineWrapSpinBox->setValue(s.lineWrapWidth); + m_ui->sshPromptChooser->setPath(s.sshPasswordPrompt); } QString CommonSettingsWidget::searchKeyWordMatchString() const diff --git a/src/plugins/vcsbase/commonsettingspage.ui b/src/plugins/vcsbase/commonsettingspage.ui index 16065aa48f80dfa53b3492fa99cd469287509c46..c15f9cd96f3fe5816f492c9a088f8d97a6a1c6bc 100644 --- a/src/plugins/vcsbase/commonsettingspage.ui +++ b/src/plugins/vcsbase/commonsettingspage.ui @@ -6,11 +6,11 @@ <rect> <x>0</x> <y>0</y> - <width>359</width> - <height>105</height> + <width>338</width> + <height>166</height> </rect> </property> - <layout class="QGridLayout" name="gridLayout"> + <layout class="QFormLayout" name="formLayout"> <item row="0" column="0"> <widget class="QCheckBox" name="lineWrapCheckBox"> <property name="text"> @@ -37,7 +37,7 @@ </property> </widget> </item> - <item row="0" column="2"> + <item row="1" column="1"> <spacer name="horizontalSpacer"> <property name="orientation"> <enum>Qt::Horizontal</enum> @@ -50,7 +50,7 @@ </property> </spacer> </item> - <item row="1" column="0"> + <item row="2" column="0"> <widget class="QLabel" name="submitMessageCheckScriptLabel"> <property name="toolTip"> <string>An executable which is called with the submit message in a temporary file as first argument. It should return with an exit != 0 and a message on standard error to indicate failure.</string> @@ -60,10 +60,10 @@ </property> </widget> </item> - <item row="1" column="1" colspan="2"> + <item row="2" column="1"> <widget class="Utils::PathChooser" name="submitMessageCheckScriptChooser" native="true"/> </item> - <item row="2" column="0"> + <item row="3" column="0"> <widget class="QLabel" name="nickNameMailMapLabel"> <property name="toolTip"> <string>A file listing user names and email addresses in a 4-column mailmap format: @@ -74,10 +74,10 @@ name <email> alias <email></string> </property> </widget> </item> - <item row="2" column="1" colspan="2"> + <item row="3" column="1"> <widget class="Utils::PathChooser" name="nickNameMailMapChooser" native="true"/> </item> - <item row="3" column="0"> + <item row="4" column="0"> <widget class="QLabel" name="nickNameFieldsFileLabel"> <property name="toolTip"> <string>A simple file containing lines with field names like "Reviewed-By:" which will be added below the submit editor.</string> @@ -87,10 +87,10 @@ name <email> alias <email></string> </property> </widget> </item> - <item row="3" column="1" colspan="2"> + <item row="4" column="1"> <widget class="Utils::PathChooser" name="nickNameFieldsFileChooser" native="true"/> </item> - <item row="4" column="0" colspan="3"> + <item row="6" column="0" colspan="2"> <spacer name="verticalSpacer"> <property name="orientation"> <enum>Qt::Vertical</enum> @@ -106,6 +106,20 @@ name <email> alias <email></string> </property> </spacer> </item> + <item row="5" column="0"> + <widget class="QLabel" name="sshPromptLabel"> + <property name="toolTip"> + <string>Specifies a command that is executed to graphically prompt for a password, +should a repository require SSH-authentication (see documentation on SSH and the environment variable SSH_ASKPASS).</string> + </property> + <property name="text"> + <string>SSH prompt command:</string> + </property> + </widget> + </item> + <item row="5" column="1"> + <widget class="Utils::PathChooser" name="sshPromptChooser" native="true"/> + </item> </layout> </widget> <customwidgets> diff --git a/src/plugins/vcsbase/commonvcssettings.cpp b/src/plugins/vcsbase/commonvcssettings.cpp index 95186a23fc3988f1f3f50b3b85aa811c2feb48dd..621e85f895f5be14d11fabf0f95eda0cc57c7281 100644 --- a/src/plugins/vcsbase/commonvcssettings.cpp +++ b/src/plugins/vcsbase/commonvcssettings.cpp @@ -32,20 +32,28 @@ #include <QtCore/QSettings> #include <QtCore/QDebug> -static const char *settingsGroupC = "VCS"; -static const char *nickNameMailMapKeyC = "NickNameMailMap"; -static const char *nickNameFieldListFileKeyC = "NickNameFieldListFile"; -static const char *submitMessageCheckScriptKeyC = "SubmitMessageCheckScript"; -static const char *lineWrapKeyC = "LineWrap"; -static const char *lineWrapWidthKeyC = "LineWrapWidth"; +static const char settingsGroupC[] = "VCS"; +static const char nickNameMailMapKeyC[] = "NickNameMailMap"; +static const char nickNameFieldListFileKeyC[] = "NickNameFieldListFile"; +static const char submitMessageCheckScriptKeyC[] = "SubmitMessageCheckScript"; +static const char lineWrapKeyC[] = "LineWrap"; +static const char lineWrapWidthKeyC[] = "LineWrapWidth"; +static const char sshPasswordPromptKeyC[] = "SshPasswordPrompt"; static const int lineWrapWidthDefault = 72; static const bool lineWrapDefault = true; +#ifdef Q_OS_WIN +static const char sshPasswordPromptDefaultC[] = "win-ssh-askpass"; +#else +static const char sshPasswordPromptDefaultC[] = "ssh-askpass"; +#endif + namespace VCSBase { namespace Internal { CommonVcsSettings::CommonVcsSettings() : + sshPasswordPrompt(QLatin1String(sshPasswordPromptDefaultC)), lineWrap(lineWrapDefault), lineWrapWidth(lineWrapWidthDefault) { @@ -59,6 +67,7 @@ void CommonVcsSettings::toSettings(QSettings *s) const s->setValue(QLatin1String(submitMessageCheckScriptKeyC), submitMessageCheckScript); s->setValue(QLatin1String(lineWrapKeyC), lineWrap); s->setValue(QLatin1String(lineWrapWidthKeyC), lineWrapWidth); + s->setValue(QLatin1String(sshPasswordPromptKeyC), sshPasswordPrompt); s->endGroup(); } @@ -70,6 +79,7 @@ void CommonVcsSettings::fromSettings(QSettings *s) submitMessageCheckScript = s->value(QLatin1String(submitMessageCheckScriptKeyC), QString()).toString(); lineWrap = s->value(QLatin1String(lineWrapKeyC), lineWrapDefault).toBool(); lineWrapWidth = s->value(QLatin1String(lineWrapWidthKeyC), lineWrapWidthDefault).toInt(); + sshPasswordPrompt = s->value(QLatin1String(sshPasswordPromptKeyC), QLatin1String(sshPasswordPromptDefaultC)).toString(); s->endGroup(); } @@ -79,7 +89,8 @@ bool CommonVcsSettings::equals(const CommonVcsSettings &rhs) const && lineWrapWidth == rhs.lineWrapWidth && nickNameMailMap == rhs.nickNameMailMap && nickNameFieldListFile == rhs.nickNameFieldListFile - && submitMessageCheckScript == rhs.submitMessageCheckScript; + && submitMessageCheckScript == rhs.submitMessageCheckScript + && sshPasswordPrompt == rhs.sshPasswordPrompt; } QDebug operator<<(QDebug d,const CommonVcsSettings& s) @@ -88,7 +99,9 @@ QDebug operator<<(QDebug d,const CommonVcsSettings& s) << " lineWrapWidth=" << s.lineWrapWidth << " nickNameMailMap='" << s.nickNameMailMap << "' nickNameFieldListFile='" << s.nickNameFieldListFile - << "'submitMessageCheckScript='" << s.submitMessageCheckScript << "'\n"; + << "'submitMessageCheckScript='" << s.submitMessageCheckScript + << "'sshPasswordPrompt='" << s.sshPasswordPrompt + << "'\n"; return d; } diff --git a/src/plugins/vcsbase/commonvcssettings.h b/src/plugins/vcsbase/commonvcssettings.h index 985683d28db2c4182477f474291dfa4cb3495ebd..d10666beae2ad6795f2bd010a034578f2754edaf 100644 --- a/src/plugins/vcsbase/commonvcssettings.h +++ b/src/plugins/vcsbase/commonvcssettings.h @@ -51,6 +51,9 @@ struct CommonVcsSettings QString submitMessageCheckScript; + // Executable run to graphically prompt for a SSH-password. + QString sshPasswordPrompt; + bool lineWrap; int lineWrapWidth; diff --git a/src/plugins/vcsbase/vcsbaseoutputwindow.cpp b/src/plugins/vcsbase/vcsbaseoutputwindow.cpp index df6f201d9ad510aea48e31871e7fa48f61978394..6f6036d3804089739ee78e2b484c2fc257806ee5 100644 --- a/src/plugins/vcsbase/vcsbaseoutputwindow.cpp +++ b/src/plugins/vcsbase/vcsbaseoutputwindow.cpp @@ -43,6 +43,8 @@ #include <QtCore/QPointer> #include <QtCore/QTextCodec> +#include <QtCore/QDir> +#include <QtCore/QTextStream> #include <QtCore/QTime> #include <QtCore/QPoint> #include <QtCore/QFileInfo> @@ -367,11 +369,53 @@ void VCSBaseOutputWindow::appendWarning(const QString &text) popup(false); // Pop up without focus } +// Helper to format arguments for log windows hiding common password +// options. +static inline QString formatArguments(const QStringList &args) +{ + const char passwordOptionC[] = "--password"; + + QString rc; + QTextStream str(&rc); + const int size = args.size(); + // Skip authentication options + for (int i = 0; i < size; i++) { + const QString &arg = args.at(i); + if (i) + str << ' '; + str << arg; + if (arg == QLatin1String(passwordOptionC)) { + str << " ********"; + i++; + } + } + return rc; +} + +QString VCSBaseOutputWindow::msgExecutionLogEntry(const QString &workingDir, + const QString &executable, + const QStringList &arguments) +{ + const QString args = formatArguments(arguments); + if (workingDir.isEmpty()) + return tr("Executing: %1 %2\n").arg(executable, args); + return tr("Executing in %1: %2 %3\n"). + arg(QDir::toNativeSeparators(workingDir), executable, args); +} + void VCSBaseOutputWindow::appendCommand(const QString &text) { d->plainTextEdit()->appendCommand(text); } +void VCSBaseOutputWindow::appendCommand(const QString &workingDirectory, + const QString &binary, + const QStringList &args) +{ + appendCommand(msgExecutionLogEntry(workingDirectory, binary, args)); +} + + void VCSBaseOutputWindow::appendData(const QByteArray &data) { appendDataSilently(data); diff --git a/src/plugins/vcsbase/vcsbaseoutputwindow.h b/src/plugins/vcsbase/vcsbaseoutputwindow.h index de65d210d0acb681283c9976112634e5c510db0d..2008c202d7db98e63da60d3d671404e25657c2ad 100644 --- a/src/plugins/vcsbase/vcsbaseoutputwindow.h +++ b/src/plugins/vcsbase/vcsbaseoutputwindow.h @@ -77,6 +77,13 @@ public: QString repository() const; + // Helper to consistently format log entries for commands as + // 'Executing <dir>: <cmd> <args>'. Hides well-known password option + // arguments. + static QString msgExecutionLogEntry(const QString &workingDir, + const QString &executable, + const QStringList &arguments); + public slots: void setRepository(const QString &); void clearRepository(); @@ -105,6 +112,11 @@ public slots: // Append a command, prepended by a log time stamp. "Executing: vcs -diff" // will result in "10:00 Executing: vcs -diff" in bold void appendCommand(const QString &text); + // Append a standard-formatted entry for command execution + // (see msgExecutionLogEntry). + void appendCommand(const QString &workingDirectory, + const QString &binary, + const QStringList &args); private: VCSBaseOutputWindow(); diff --git a/src/plugins/vcsbase/vcsbaseplugin.cpp b/src/plugins/vcsbase/vcsbaseplugin.cpp index 7b1c3f40c1c3be467ed78692afdc2ab758fb0b1c..6f5d24c48f1839850e6e80196a61eaf8f41d3f72 100644 --- a/src/plugins/vcsbase/vcsbaseplugin.cpp +++ b/src/plugins/vcsbase/vcsbaseplugin.cpp @@ -30,6 +30,7 @@ #include "vcsbaseplugin.h" #include "vcsbasesubmiteditor.h" #include "vcsplugin.h" +#include "commonvcssettings.h" #include "vcsbaseoutputwindow.h" #include "corelistener.h" @@ -43,18 +44,22 @@ #include <projectexplorer/projectexplorer.h> #include <projectexplorer/project.h> #include <utils/qtcassert.h> +#include <utils/synchronousprocess.h> #include <QtCore/QDebug> #include <QtCore/QDir> #include <QtCore/QSharedData> #include <QtCore/QScopedPointer> +#include <QtCore/QProcessEnvironment> +#include <QtCore/QTextStream> +#include <QtCore/QTextCodec> #include <QtGui/QAction> #include <QtGui/QMessageBox> #include <QtGui/QFileDialog> #include <QtGui/QMainWindow> -enum { debug = 0, debugRepositorySearch = 0 }; +enum { debug = 0, debugRepositorySearch = 0, debugExecution = 0 }; namespace VCSBase { @@ -670,6 +675,116 @@ QString VCSBasePlugin::findRepositoryForDirectory(const QString &dirS, return QString(); } +// Is SSH prompt configured? +static inline QString sshPrompt() +{ + return VCSBase::Internal::VCSPlugin::instance()->settings().sshPasswordPrompt; +} + +bool VCSBasePlugin::isSshPromptConfigured() +{ + return !sshPrompt().isEmpty(); +} + +void VCSBasePlugin::setProcessEnvironment(QProcessEnvironment *e) +{ + e->insert(QLatin1String("LANG"), QString(QLatin1Char('C'))); + const QString sshPromptBinary = sshPrompt(); + if (!sshPromptBinary.isEmpty()) + e->insert(QLatin1String("SSH_ASKPASS"), sshPromptBinary); +} + +Utils::SynchronousProcessResponse + VCSBasePlugin::runVCS(const QString &workingDir, + const QString &binary, + const QStringList &arguments, + int timeOutMS, + unsigned flags, + QTextCodec *outputCodec /* = 0 */) +{ + const QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + return runVCS(workingDir, binary, arguments, timeOutMS, env, + flags, outputCodec); +} + +Utils::SynchronousProcessResponse + VCSBasePlugin::runVCS(const QString &workingDir, + const QString &binary, + const QStringList &arguments, + int timeOutMS, + QProcessEnvironment env, + unsigned flags, + QTextCodec *outputCodec /* = 0 */) +{ + VCSBase::VCSBaseOutputWindow *outputWindow = VCSBase::VCSBaseOutputWindow::instance(); + + if (!(flags & SuppressCommandLogging)) + outputWindow->appendCommand(workingDir, binary, arguments); + + const bool sshPromptConfigured = VCSBasePlugin::isSshPromptConfigured(); + 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 (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); + 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))); + } + + process.setTimeOutMessageBoxEnabled(true); + + // Run! + const Utils::SynchronousProcessResponse sp_resp = process.run(binary, arguments); + + // Fail message? + if (sp_resp.result != Utils::SynchronousProcessResponse::Finished && + (!(flags & SuppressFailMessageInLogWindow))) + outputWindow->appendError(sp_resp.exitMessage(binary, timeOutMS)); + + return sp_resp; +} } // namespace VCSBase #include "vcsbaseplugin.moc" diff --git a/src/plugins/vcsbase/vcsbaseplugin.h b/src/plugins/vcsbase/vcsbaseplugin.h index 5d697fa3af9dbdfde40f34975bd386b39be70ce9..2e090fe62202b29889f85ed8cf3c981731739714 100644 --- a/src/plugins/vcsbase/vcsbaseplugin.h +++ b/src/plugins/vcsbase/vcsbaseplugin.h @@ -39,8 +39,14 @@ QT_BEGIN_NAMESPACE class QAction; +class QProcessEnvironment; +class QTextCodec; QT_END_NAMESPACE +namespace Utils { + struct SynchronousProcessResponse; +} + namespace Core { class IVersionControl; } @@ -169,6 +175,41 @@ public: // a well known file. See implementation for gory details. static QString findRepositoryForDirectory(const QString &dir, const QString &checkFile); + // Set up the environment for a version control command line call. + // Sets LANG to 'C' to force English (suppress LOCALE warnings) + // and sets up SSH graphical password prompting (note that the latter + // requires a terminal-less process). + static void setProcessEnvironment(QProcessEnvironment *e); + // Returns whether an SSH prompt is configured. + static bool isSshPromptConfigured(); + + // Convenience to synchronously run VCS commands + enum RunVCSFlags { + ShowStdOutInLogWindow = 0x1, // Append standard output to VCS output window. + MergeOutputChannels = 0x2, // see QProcess: Merge stderr/stdout. + SshPasswordPrompt = 0x40, // Disable terminal on UNIX to force graphical prompt. + SuppressStdErrInLogWindow = 0x8, // No standard error output to VCS output window. + SuppressFailMessageInLogWindow = 0x10, // No message VCS about failure in VCS output window. + SuppressCommandLogging = 0x20 // No command log entry in VCS output window. + }; + + static Utils::SynchronousProcessResponse + runVCS(const QString &workingDir, + const QString &binary, + const QStringList &arguments, + int timeOutMS, + QProcessEnvironment env, + unsigned flags = 0, + QTextCodec *outputCodec = 0); + + static Utils::SynchronousProcessResponse + runVCS(const QString &workingDir, + const QString &binary, + const QStringList &arguments, + int timeOutMS, + unsigned flags = 0, + QTextCodec *outputCodec = 0); + public slots: // Convenience slot for "Delete current file" action. Prompts to // delete the file via VCSManager. diff --git a/src/plugins/vcsbase/vcsbasesubmiteditor.cpp b/src/plugins/vcsbase/vcsbasesubmiteditor.cpp index 4ad802e32f696c6e27d9fbad966576ab3b784125..98241014c8cd89c3c16f410c22646a202b07b562 100644 --- a/src/plugins/vcsbase/vcsbasesubmiteditor.cpp +++ b/src/plugins/vcsbase/vcsbasesubmiteditor.cpp @@ -584,7 +584,7 @@ bool VCSBaseSubmitEditor::runSubmitMessageCheckScript(const QString &checkScript } QByteArray stdOutData; QByteArray stdErrData; - if (!Utils::SynchronousProcess::readDataFromProcess(checkProcess, 30000, &stdOutData, &stdErrData)) { + if (!Utils::SynchronousProcess::readDataFromProcess(checkProcess, 30000, &stdOutData, &stdErrData, false)) { Utils::SynchronousProcess::stopProcess(checkProcess); *errorMessage = tr("The check script '%1' timed out.").arg(checkScript); return false;