1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights. These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
32 **************************************************************************/
34 #include "vcsbaseplugin.h"
35 #include "vcsbasesubmiteditor.h"
36 #include "vcsplugin.h"
37 #include "commonvcssettings.h"
38 #include "vcsbaseoutputwindow.h"
39 #include "corelistener.h"
41 #include <coreplugin/icore.h>
42 #include <coreplugin/ifile.h>
43 #include <coreplugin/iversioncontrol.h>
44 #include <coreplugin/filemanager.h>
45 #include <coreplugin/editormanager/editormanager.h>
46 #include <coreplugin/editormanager/ieditor.h>
47 #include <coreplugin/vcsmanager.h>
48 #include <projectexplorer/projectexplorer.h>
49 #include <projectexplorer/project.h>
50 #include <utils/qtcassert.h>
51 #include <utils/synchronousprocess.h>
53 #include <QtCore/QDebug>
54 #include <QtCore/QDir>
55 #include <QtCore/QSharedData>
56 #include <QtCore/QScopedPointer>
57 #include <QtCore/QSharedPointer>
58 #include <QtCore/QProcessEnvironment>
59 #include <QtCore/QTextStream>
60 #include <QtCore/QTextCodec>
62 #include <QtGui/QAction>
63 #include <QtGui/QMessageBox>
64 #include <QtGui/QFileDialog>
65 #include <QtGui/QMainWindow>
67 enum { debug = 0, debugRepositorySearch = 0, debugExecution = 0 };
73 // Internal state created by the state listener and
74 // VCSBasePluginState.
78 void clearPatchFile();
82 bool equals(const State &rhs) const;
84 inline bool hasFile() const { return !currentFile.isEmpty(); }
85 inline bool hasProject() const { return !currentProjectPath.isEmpty(); }
86 inline bool isEmpty() const { return !hasFile() && !hasProject(); }
89 QString currentFileName;
90 QString currentPatchFile;
91 QString currentPatchFileDisplayName;
93 QString currentFileDirectory;
94 QString currentFileTopLevel;
96 QString currentProjectPath;
97 QString currentProjectName;
98 QString currentProjectTopLevel;
101 void State::clearFile()
104 currentFileName.clear();
105 currentFileDirectory.clear();
106 currentFileTopLevel.clear();
109 void State::clearPatchFile()
111 currentPatchFile.clear();
112 currentPatchFileDisplayName.clear();
115 void State::clearProject()
117 currentProjectPath.clear();
118 currentProjectName.clear();
119 currentProjectTopLevel.clear();
129 bool State::equals(const State &rhs) const
131 return currentFile == rhs.currentFile
132 && currentFileName == rhs.currentFileName
133 && currentPatchFile == rhs.currentPatchFile
134 && currentPatchFileDisplayName == rhs.currentPatchFileDisplayName
135 && currentFileTopLevel == rhs.currentFileTopLevel
136 && currentProjectPath == rhs.currentProjectPath
137 && currentProjectName == rhs.currentProjectName
138 && currentProjectTopLevel == rhs.currentProjectTopLevel;
141 QDebug operator<<(QDebug in, const State &state)
143 QDebug nospace = in.nospace();
144 nospace << "State: ";
145 if (state.isEmpty()) {
146 nospace << "<empty>";
148 if (state.hasFile()) {
149 nospace << "File=" << state.currentFile
150 << ',' << state.currentFileTopLevel;
152 nospace << "<no file>";
155 if (state.hasProject()) {
156 nospace << " Project=" << state.currentProjectName
157 << ',' << state.currentProjectPath
158 << ',' << state.currentProjectTopLevel;
161 nospace << "<no project>";
168 // StateListener: Connects to the relevant signals, tries to find version
169 // controls and emits signals to the plugin instances.
171 class StateListener : public QObject {
174 explicit StateListener(QObject *parent);
177 void stateChanged(const VCSBase::Internal::State &s, Core::IVersionControl *vc);
180 void slotStateChanged();
183 StateListener::StateListener(QObject *parent) :
186 Core::ICore *core = Core::ICore::instance();
187 connect(core->fileManager(), SIGNAL(currentFileChanged(QString)),
188 this, SLOT(slotStateChanged()));
189 connect(core->editorManager()->instance(), SIGNAL(currentEditorStateChanged(Core::IEditor*)),
190 this, SLOT(slotStateChanged()));
192 if (ProjectExplorer::ProjectExplorerPlugin *pe = ProjectExplorer::ProjectExplorerPlugin::instance())
193 connect(pe, SIGNAL(currentProjectChanged(ProjectExplorer::Project*)),
194 this, SLOT(slotStateChanged()));
197 static inline QString displayNameOfEditor(const QString &fileName)
199 const QList<Core::IEditor*> editors = Core::EditorManager::instance()->editorsForFileName(fileName);
200 if (!editors.isEmpty())
201 return editors.front()->displayName();
205 void StateListener::slotStateChanged()
207 const ProjectExplorer::ProjectExplorerPlugin *pe = ProjectExplorer::ProjectExplorerPlugin::instance();
208 const Core::ICore *core = Core::ICore::instance();
209 Core::VcsManager *vcsManager = core->vcsManager();
211 // Get the current file. Are we on a temporary submit editor indicated by
212 // temporary path prefix or does the file contains a hash, indicating a project
215 state.currentFile = core->fileManager()->currentFile();
216 QScopedPointer<QFileInfo> currentFileInfo; // Instantiate QFileInfo only once if required.
217 if (!state.currentFile.isEmpty()) {
218 const bool isTempFile = state.currentFile.startsWith(QDir::tempPath());
219 // Quick check: Does it look like a patch?
220 const bool isPatch = state.currentFile.endsWith(QLatin1String(".patch"))
221 || state.currentFile.endsWith(QLatin1String(".diff"));
223 // Patch: Figure out a name to display. If it is a temp file, it could be
224 // Codepaster. Use the display name of the editor.
225 state.currentPatchFile = state.currentFile;
227 state.currentPatchFileDisplayName = displayNameOfEditor(state.currentPatchFile);
228 if (state.currentPatchFileDisplayName.isEmpty()) {
229 currentFileInfo.reset(new QFileInfo(state.currentFile));
230 state.currentPatchFileDisplayName = currentFileInfo->fileName();
233 // For actual version control operations on it:
234 // Do not show temporary files and project folders ('#')
235 if (isTempFile || state.currentFile.contains(QLatin1Char('#')))
236 state.currentFile.clear();
239 // Get the file and its control. Do not use the file unless we find one
240 Core::IVersionControl *fileControl = 0;
241 if (!state.currentFile.isEmpty()) {
242 if (currentFileInfo.isNull())
243 currentFileInfo.reset(new QFileInfo(state.currentFile));
244 state.currentFileDirectory = currentFileInfo->absolutePath();
245 state.currentFileName = currentFileInfo->fileName();
246 fileControl = vcsManager->findVersionControlForDirectory(state.currentFileDirectory,
247 &state.currentFileTopLevel);
251 // Check for project, find the control
252 Core::IVersionControl *projectControl = 0;
253 if (const ProjectExplorer::Project *currentProject = pe->currentProject()) {
254 state.currentProjectPath = currentProject->projectDirectory();
255 state.currentProjectName = currentProject->displayName();
256 projectControl = vcsManager->findVersionControlForDirectory(state.currentProjectPath,
257 &state.currentProjectTopLevel);
258 if (projectControl) {
259 // If we have both, let the file's one take preference
260 if (fileControl && projectControl != fileControl) {
261 state.clearProject();
264 state.clearProject(); // No control found
267 // Assemble state and emit signal.
268 Core::IVersionControl *vc = state.currentFile.isEmpty() ? projectControl : fileControl;
269 if (!vc) // Need a repository to patch
270 state.clearPatchFile();
272 qDebug() << state << (vc ? vc->displayName() : QString(QLatin1String("No version control")));
273 emit stateChanged(state, vc);
276 } // namespace Internal
278 class VCSBasePluginStateData : public QSharedData {
280 Internal::State m_state;
283 VCSBasePluginState::VCSBasePluginState() : data(new VCSBasePluginStateData)
287 VCSBasePluginState::VCSBasePluginState(const VCSBasePluginState &rhs) : data(rhs.data)
291 VCSBasePluginState &VCSBasePluginState::operator=(const VCSBasePluginState &rhs)
294 data.operator=(rhs.data);
298 VCSBasePluginState::~VCSBasePluginState()
302 QString VCSBasePluginState::currentFile() const
304 return data->m_state.currentFile;
307 QString VCSBasePluginState::currentFileName() const
309 return data->m_state.currentFileName;
312 QString VCSBasePluginState::currentFileTopLevel() const
314 return data->m_state.currentFileTopLevel;
317 QString VCSBasePluginState::currentFileDirectory() const
319 return data->m_state.currentFileDirectory;
322 QString VCSBasePluginState::relativeCurrentFile() const
324 QTC_ASSERT(hasFile(), return QString())
325 return QDir(data->m_state.currentFileTopLevel).relativeFilePath(data->m_state.currentFile);
328 QString VCSBasePluginState::currentPatchFile() const
330 return data->m_state.currentPatchFile;
333 QString VCSBasePluginState::currentPatchFileDisplayName() const
335 return data->m_state.currentPatchFileDisplayName;
338 QString VCSBasePluginState::currentProjectPath() const
340 return data->m_state.currentProjectPath;
343 QString VCSBasePluginState::currentProjectName() const
345 return data->m_state.currentProjectName;
348 QString VCSBasePluginState::currentProjectTopLevel() const
350 return data->m_state.currentProjectTopLevel;
353 QStringList VCSBasePluginState::relativeCurrentProject() const
356 QTC_ASSERT(hasProject(), return rc)
357 if (data->m_state.currentProjectTopLevel != data->m_state.currentProjectPath)
358 rc.append(QDir(data->m_state.currentProjectTopLevel).relativeFilePath(data->m_state.currentProjectPath));
362 bool VCSBasePluginState::hasTopLevel() const
364 return data->m_state.hasFile() || data->m_state.hasProject();
367 QString VCSBasePluginState::topLevel() const
369 return hasFile() ? data->m_state.currentFileTopLevel : data->m_state.currentProjectTopLevel;
372 bool VCSBasePluginState::equals(const Internal::State &rhs) const
374 return data->m_state.equals(rhs);
377 bool VCSBasePluginState::equals(const VCSBasePluginState &rhs) const
379 return equals(rhs.data->m_state);
382 void VCSBasePluginState::clear()
384 data->m_state.clear();
387 void VCSBasePluginState::setState(const Internal::State &s)
392 bool VCSBasePluginState::isEmpty() const
394 return data->m_state.isEmpty();
397 bool VCSBasePluginState::hasFile() const
399 return data->m_state.hasFile();
402 bool VCSBasePluginState::hasPatchFile() const
404 return !data->m_state.currentPatchFile.isEmpty();
407 bool VCSBasePluginState::hasProject() const
409 return data->m_state.hasProject();
412 VCSBASE_EXPORT QDebug operator<<(QDebug in, const VCSBasePluginState &state)
414 in << state.data->m_state;
419 struct VCSBasePluginPrivate {
420 explicit VCSBasePluginPrivate(const QString &submitEditorId);
422 inline bool supportsRepositoryCreation() const;
424 const QString m_submitEditorId;
425 Core::IVersionControl *m_versionControl;
426 VCSBasePluginState m_state;
428 QAction *m_testSnapshotAction;
429 QAction *m_testListSnapshotsAction;
430 QAction *m_testRestoreSnapshotAction;
431 QAction *m_testRemoveSnapshotAction;
432 QString m_testLastSnapshot;
434 static Internal::StateListener *m_listener;
437 VCSBasePluginPrivate::VCSBasePluginPrivate(const QString &submitEditorId) :
438 m_submitEditorId(submitEditorId),
441 m_testSnapshotAction(0),
442 m_testListSnapshotsAction(0),
443 m_testRestoreSnapshotAction(0),
444 m_testRemoveSnapshotAction(0)
448 bool VCSBasePluginPrivate::supportsRepositoryCreation() const
450 return m_versionControl && m_versionControl->supportsOperation(Core::IVersionControl::CreateRepositoryOperation);
453 Internal::StateListener *VCSBasePluginPrivate::m_listener = 0;
455 VCSBasePlugin::VCSBasePlugin(const QString &submitEditorId) :
456 d(new VCSBasePluginPrivate(submitEditorId))
460 VCSBasePlugin::~VCSBasePlugin()
465 void VCSBasePlugin::initialize(Core::IVersionControl *vc)
467 d->m_versionControl = vc;
468 addAutoReleasedObject(vc);
470 Internal::VCSPlugin *plugin = Internal::VCSPlugin::instance();
471 connect(plugin->coreListener(), SIGNAL(submitEditorAboutToClose(VCSBaseSubmitEditor*,bool*)),
472 this, SLOT(slotSubmitEditorAboutToClose(VCSBaseSubmitEditor*,bool*)));
473 // First time: create new listener
474 if (!VCSBasePluginPrivate::m_listener)
475 VCSBasePluginPrivate::m_listener = new Internal::StateListener(plugin);
476 connect(VCSBasePluginPrivate::m_listener,
477 SIGNAL(stateChanged(VCSBase::Internal::State, Core::IVersionControl*)),
479 SLOT(slotStateChanged(VCSBase::Internal::State,Core::IVersionControl*)));
482 void VCSBasePlugin::extensionsInitialized()
484 // Initialize enable menus.
485 VCSBasePluginPrivate::m_listener->slotStateChanged();
488 void VCSBasePlugin::slotSubmitEditorAboutToClose(VCSBaseSubmitEditor *submitEditor, bool *result)
491 qDebug() << this << d->m_submitEditorId << "Closing submit editor" << submitEditor << submitEditor->id();
492 if (submitEditor->id() == d->m_submitEditorId)
493 *result = submitEditorAboutToClose(submitEditor);
496 Core::IVersionControl *VCSBasePlugin::versionControl() const
498 return d->m_versionControl;
501 void VCSBasePlugin::slotStateChanged(const VCSBase::Internal::State &newInternalState, Core::IVersionControl *vc)
503 if (vc == d->m_versionControl) {
504 // We are directly affected: Change state
505 if (!d->m_state.equals(newInternalState)) {
506 d->m_state.setState(newInternalState);
507 updateActions(VCSEnabled);
510 // Some other VCS plugin or state changed: Reset us to empty state.
511 const ActionState newActionState = vc ? OtherVCSEnabled : NoVCSEnabled;
512 if (d->m_actionState != newActionState || !d->m_state.isEmpty()) {
513 d->m_actionState = newActionState;
514 const VCSBasePluginState emptyState;
515 d->m_state = emptyState;
516 updateActions(newActionState);
521 const VCSBasePluginState &VCSBasePlugin::currentState() const
526 bool VCSBasePlugin::enableMenuAction(ActionState as, QAction *menuAction) const
529 qDebug() << "enableMenuAction" << menuAction->text() << as;
531 case VCSBase::VCSBasePlugin::NoVCSEnabled: {
532 const bool supportsCreation = d->supportsRepositoryCreation();
533 menuAction->setVisible(supportsCreation);
534 menuAction->setEnabled(supportsCreation);
535 return supportsCreation;
537 case VCSBase::VCSBasePlugin::OtherVCSEnabled:
538 menuAction->setVisible(false);
540 case VCSBase::VCSBasePlugin::VCSEnabled:
541 menuAction->setVisible(true);
542 menuAction->setEnabled(true);
548 void VCSBasePlugin::promptToDeleteCurrentFile()
550 const VCSBasePluginState state = currentState();
551 QTC_ASSERT(state.hasFile(), return)
552 const bool rc = Core::ICore::instance()->vcsManager()->promptToDelete(versionControl(), state.currentFile());
554 QMessageBox::warning(0, tr("Version Control"),
555 tr("The file '%1' could not be deleted.").
556 arg(QDir::toNativeSeparators(state.currentFile())),
560 static inline bool ask(QWidget *parent, const QString &title, const QString &question, bool defaultValue = true)
563 const QMessageBox::StandardButton defaultButton = defaultValue ? QMessageBox::Yes : QMessageBox::No;
564 return QMessageBox::question(parent, title, question, QMessageBox::Yes|QMessageBox::No, defaultButton) == QMessageBox::Yes;
567 void VCSBasePlugin::createRepository()
569 QTC_ASSERT(d->m_versionControl->supportsOperation(Core::IVersionControl::CreateRepositoryOperation), return);
570 // Find current starting directory
572 if (const ProjectExplorer::Project *currentProject = ProjectExplorer::ProjectExplorerPlugin::instance()->currentProject())
573 directory = QFileInfo(currentProject->file()->fileName()).absolutePath();
574 // Prompt for a directory that is not under version control yet
575 QMainWindow *mw = Core::ICore::instance()->mainWindow();
577 directory = QFileDialog::getExistingDirectory(mw, tr("Choose Repository Directory"), directory);
578 if (directory.isEmpty())
580 const Core::IVersionControl *managingControl = Core::ICore::instance()->vcsManager()->findVersionControlForDirectory(directory);
581 if (managingControl == 0)
583 const QString question = tr("The directory '%1' is already managed by a version control system (%2)."
584 " Would you like to specify another directory?").arg(directory, managingControl->displayName());
586 if (!ask(mw, tr("Repository already under version control"), question))
590 const bool rc = d->m_versionControl->vcsCreateRepository(directory);
591 const QString nativeDir = QDir::toNativeSeparators(directory);
593 QMessageBox::information(mw, tr("Repository Created"),
594 tr("A version control repository has been created in %1.").
597 QMessageBox::warning(mw, tr("Repository Creation Failed"),
598 tr("A version control repository could not be created in %1.").
603 // For internal tests: Create actions driving IVersionControl's snapshot interface.
604 QList<QAction*> VCSBasePlugin::createSnapShotTestActions()
606 if (!d->m_testSnapshotAction) {
607 d->m_testSnapshotAction = new QAction(QLatin1String("Take snapshot"), this);
608 connect(d->m_testSnapshotAction, SIGNAL(triggered()), this, SLOT(slotTestSnapshot()));
609 d->m_testListSnapshotsAction = new QAction(QLatin1String("List snapshots"), this);
610 connect(d->m_testListSnapshotsAction, SIGNAL(triggered()), this, SLOT(slotTestListSnapshots()));
611 d->m_testRestoreSnapshotAction = new QAction(QLatin1String("Restore snapshot"), this);
612 connect(d->m_testRestoreSnapshotAction, SIGNAL(triggered()), this, SLOT(slotTestRestoreSnapshot()));
613 d->m_testRemoveSnapshotAction = new QAction(QLatin1String("Remove snapshot"), this);
614 connect(d->m_testRemoveSnapshotAction, SIGNAL(triggered()), this, SLOT(slotTestRemoveSnapshot()));
617 rc << d->m_testSnapshotAction << d->m_testListSnapshotsAction << d->m_testRestoreSnapshotAction
618 << d->m_testRemoveSnapshotAction;
622 void VCSBasePlugin::slotTestSnapshot()
624 QTC_ASSERT(currentState().hasTopLevel(), return)
625 d->m_testLastSnapshot = versionControl()->vcsCreateSnapshot(currentState().topLevel());
626 qDebug() << "Snapshot " << d->m_testLastSnapshot;
627 VCSBaseOutputWindow::instance()->append(QLatin1String("Snapshot: ") + d->m_testLastSnapshot);
628 if (!d->m_testLastSnapshot.isEmpty())
629 d->m_testRestoreSnapshotAction->setText(QLatin1String("Restore snapshot ") + d->m_testLastSnapshot);
632 void VCSBasePlugin::slotTestListSnapshots()
634 QTC_ASSERT(currentState().hasTopLevel(), return)
635 const QStringList snapshots = versionControl()->vcsSnapshots(currentState().topLevel());
636 qDebug() << "Snapshots " << snapshots;
637 VCSBaseOutputWindow::instance()->append(QLatin1String("Snapshots: ") + snapshots.join(QLatin1String(", ")));
640 void VCSBasePlugin::slotTestRestoreSnapshot()
642 QTC_ASSERT(currentState().hasTopLevel() && !d->m_testLastSnapshot.isEmpty(), return)
643 const bool ok = versionControl()->vcsRestoreSnapshot(currentState().topLevel(), d->m_testLastSnapshot);
644 const QString msg = d->m_testLastSnapshot+ (ok ? QLatin1String(" restored") : QLatin1String(" failed"));
646 VCSBaseOutputWindow::instance()->append(msg);
649 void VCSBasePlugin::slotTestRemoveSnapshot()
651 QTC_ASSERT(currentState().hasTopLevel() && !d->m_testLastSnapshot.isEmpty(), return)
652 const bool ok = versionControl()->vcsRemoveSnapshot(currentState().topLevel(), d->m_testLastSnapshot);
653 const QString msg = d->m_testLastSnapshot+ (ok ? QLatin1String(" removed") : QLatin1String(" failed"));
655 VCSBaseOutputWindow::instance()->append(msg);
656 d->m_testLastSnapshot.clear();
659 // Find top level for version controls like git/Mercurial that have
660 // a directory at the top of the repository.
661 // Note that checking for the existence of files is preferred over directories
662 // since checking for directories can cause them to be created when
663 // AutoFS is used (due its automatically creating mountpoints when querying
664 // a directory). In addition, bail out when reaching the home directory
665 // of the user or root (generally avoid '/', where mountpoints are created).
666 QString VCSBasePlugin::findRepositoryForDirectory(const QString &dirS,
667 const QString &checkFile)
669 if (debugRepositorySearch)
670 qDebug() << ">VCSBasePlugin::findRepositoryForDirectory" << dirS << checkFile;
671 QTC_ASSERT(!dirS.isEmpty() && !checkFile.isEmpty(), return QString());
673 const QString root = QDir::rootPath();
674 const QString home = QDir::homePath();
676 QDir directory(dirS);
678 const QString absDirPath = directory.absolutePath();
679 if (absDirPath == root || absDirPath == home)
682 if (QFileInfo(directory, checkFile).isFile()) {
683 if (debugRepositorySearch)
684 qDebug() << "<VCSBasePlugin::findRepositoryForDirectory> " << absDirPath;
687 } while (directory.cdUp());
688 if (debugRepositorySearch)
689 qDebug() << "<VCSBasePlugin::findRepositoryForDirectory bailing out at " << directory.absolutePath();
693 // Is SSH prompt configured?
694 static inline QString sshPrompt()
696 return VCSBase::Internal::VCSPlugin::instance()->settings().sshPasswordPrompt;
699 bool VCSBasePlugin::isSshPromptConfigured()
701 return !sshPrompt().isEmpty();
704 void VCSBasePlugin::setProcessEnvironment(QProcessEnvironment *e, bool forceCLocale)
707 e->insert(QLatin1String("LANG"), QString(QLatin1Char('C')));
708 const QString sshPromptBinary = sshPrompt();
709 if (!sshPromptBinary.isEmpty())
710 e->insert(QLatin1String("SSH_ASKPASS"), sshPromptBinary);
713 // Run a process fully synchronously, returning Utils::SynchronousProcessResponse
714 // response struct and using the VCSBasePlugin flags as applicable
715 static Utils::SynchronousProcessResponse
716 runVCS_FullySynchronously(const QString &workingDir,
717 const QString &binary,
718 const QStringList &arguments,
720 QProcessEnvironment env,
722 QTextCodec *outputCodec = 0)
724 VCSBase::VCSBaseOutputWindow *outputWindow = VCSBase::VCSBaseOutputWindow::instance();
727 unsigned processFlags = 0;
728 if (VCSBasePlugin::isSshPromptConfigured() && (flags & VCSBasePlugin::SshPasswordPrompt))
729 processFlags |= Utils::SynchronousProcess::UnixTerminalDisabled;
730 QSharedPointer<QProcess> process = Utils::SynchronousProcess::createProcess(processFlags);
731 if (!workingDir.isEmpty())
732 process->setWorkingDirectory(workingDir);
733 process->setProcessEnvironment(env);
734 if (flags & VCSBasePlugin::MergeOutputChannels)
735 process->setProcessChannelMode(QProcess::MergedChannels);
738 process->start(binary, arguments);
739 Utils::SynchronousProcessResponse response;
740 if (!process->waitForStarted()) {
741 response.result = Utils::SynchronousProcessResponse::StartFailed;
748 const bool timedOut =
749 !Utils::SynchronousProcess::readDataFromProcess(*process.data(), timeOutMS,
750 &stdOut, &stdErr, true);
752 if (!stdErr.isEmpty()) {
753 response.stdErr = QString::fromLocal8Bit(stdErr).remove('\r');
754 if (!(flags & VCSBasePlugin::SuppressStdErrInLogWindow))
755 outputWindow->append(response.stdErr);
758 if (!stdOut.isEmpty()) {
759 response.stdOut = (outputCodec ? outputCodec->toUnicode(stdOut) : QString::fromLocal8Bit(stdOut))
761 if (flags & VCSBasePlugin::ShowStdOutInLogWindow)
762 outputWindow->append(response.stdOut);
767 response.result = Utils::SynchronousProcessResponse::Hang;
768 } else if (process->exitStatus() != QProcess::NormalExit) {
769 response.result = Utils::SynchronousProcessResponse::TerminatedAbnormally;
771 response.result = process->exitCode() == 0 ?
772 Utils::SynchronousProcessResponse::Finished :
773 Utils::SynchronousProcessResponse::FinishedError;
779 Utils::SynchronousProcessResponse
780 VCSBasePlugin::runVCS(const QString &workingDir,
781 const QString &binary,
782 const QStringList &arguments,
785 QTextCodec *outputCodec /* = 0 */)
787 const QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
788 return runVCS(workingDir, binary, arguments, timeOutMS, env,
792 Utils::SynchronousProcessResponse
793 VCSBasePlugin::runVCS(const QString &workingDir,
794 const QString &binary,
795 const QStringList &arguments,
797 QProcessEnvironment env,
799 QTextCodec *outputCodec /* = 0 */)
801 VCSBase::VCSBaseOutputWindow *outputWindow = VCSBase::VCSBaseOutputWindow::instance();
803 if (!(flags & SuppressCommandLogging))
804 outputWindow->appendCommand(workingDir, binary, arguments);
806 const bool sshPromptConfigured = VCSBasePlugin::isSshPromptConfigured();
807 if (debugExecution) {
808 QDebug nsp = qDebug().nospace();
809 nsp << "VCSBasePlugin::runVCS" << workingDir << binary << arguments
811 if (flags & ShowStdOutInLogWindow)
813 if (flags & SuppressStdErrInLogWindow)
814 nsp << "suppress_stderr";
815 if (flags & SuppressFailMessageInLogWindow)
816 nsp << "suppress_fail_msg";
817 if (flags & MergeOutputChannels)
818 nsp << "merge_channels";
819 if (flags & SshPasswordPrompt)
820 nsp << "ssh (" << sshPromptConfigured << ')';
821 if (flags & SuppressCommandLogging)
822 nsp << "suppress_log";
823 if (flags & ForceCLocale)
825 if (flags & FullySynchronously)
826 nsp << "fully_synchronously";
828 nsp << " Codec: " << outputCodec->name();
831 VCSBase::VCSBasePlugin::setProcessEnvironment(&env, (flags & ForceCLocale));
833 Utils::SynchronousProcessResponse response;
835 if (flags & FullySynchronously) {
836 response = runVCS_FullySynchronously(workingDir, binary, arguments, timeOutMS,
837 env, flags, outputCodec);
839 // Run, connect stderr to the output window
840 Utils::SynchronousProcess process;
841 if (!workingDir.isEmpty())
842 process.setWorkingDirectory(workingDir);
844 process.setProcessEnvironment(env);
845 process.setTimeout(timeOutMS);
847 process.setStdOutCodec(outputCodec);
849 // Suppress terminal on UNIX for ssh prompts if it is configured.
850 if (sshPromptConfigured && (flags & SshPasswordPrompt))
851 process.setFlags(Utils::SynchronousProcess::UnixTerminalDisabled);
853 // connect stderr to the output window if desired
854 if (flags & MergeOutputChannels) {
855 process.setProcessChannelMode(QProcess::MergedChannels);
857 if (!(flags & SuppressStdErrInLogWindow)) {
858 process.setStdErrBufferedSignalsEnabled(true);
859 connect(&process, SIGNAL(stdErrBuffered(QString,bool)), outputWindow, SLOT(append(QString)));
863 // connect stdout to the output window if desired
864 if (flags & ShowStdOutInLogWindow) {
865 process.setStdOutBufferedSignalsEnabled(true);
866 connect(&process, SIGNAL(stdOutBuffered(QString,bool)), outputWindow, SLOT(append(QString)));
869 process.setTimeOutMessageBoxEnabled(true);
872 response = process.run(binary, arguments);
875 // Success/Fail message in appropriate window?
876 if (response.result == Utils::SynchronousProcessResponse::Finished) {
877 if (flags & ShowSuccessMessage)
878 outputWindow->append(response.exitMessage(binary, timeOutMS));
880 if (!(flags & SuppressFailMessageInLogWindow))
881 outputWindow->appendError(response.exitMessage(binary, timeOutMS));
886 } // namespace VCSBase
888 #include "vcsbaseplugin.moc"