2009-11-02 18:50:06 +01:00
|
|
|
/**************************************************************************
|
|
|
|
|
**
|
|
|
|
|
** This file is part of Qt Creator
|
|
|
|
|
**
|
|
|
|
|
** Copyright (c) 2009 Brian McGillion
|
|
|
|
|
**
|
|
|
|
|
** Contact: Nokia Corporation (qt-info@nokia.com)
|
|
|
|
|
**
|
|
|
|
|
** Commercial Usage
|
|
|
|
|
**
|
|
|
|
|
** Licensees holding valid Qt Commercial licenses may use this file in
|
|
|
|
|
** accordance with the Qt Commercial License Agreement provided with the
|
|
|
|
|
** Software or, alternatively, in accordance with the terms contained in
|
|
|
|
|
** a written agreement between you and Nokia.
|
|
|
|
|
**
|
|
|
|
|
** GNU Lesser General Public License Usage
|
|
|
|
|
**
|
|
|
|
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
|
|
|
|
** General Public License version 2.1 as published by the Free Software
|
|
|
|
|
** Foundation and appearing in the file LICENSE.LGPL included in the
|
|
|
|
|
** packaging of this file. Please review the following information to
|
|
|
|
|
** ensure the GNU Lesser General Public License version 2.1 requirements
|
|
|
|
|
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
|
|
|
|
**
|
|
|
|
|
** If you are unsure which license is appropriate for your use, please
|
|
|
|
|
** contact the sales department at http://qt.nokia.com/contact.
|
|
|
|
|
**
|
|
|
|
|
**************************************************************************/
|
|
|
|
|
|
2009-09-15 13:03:13 +03:00
|
|
|
#include "mercurialjobrunner.h"
|
|
|
|
|
#include "mercurialplugin.h"
|
|
|
|
|
#include "constants.h"
|
|
|
|
|
#include "mercurialsettings.h"
|
|
|
|
|
|
2009-11-03 14:21:48 +01:00
|
|
|
#include <vcsbase/vcsbaseoutputwindow.h>
|
2009-09-15 13:03:13 +03:00
|
|
|
#include <vcsbase/vcsbaseeditor.h>
|
|
|
|
|
|
|
|
|
|
#include <QtCore/QProcess>
|
|
|
|
|
#include <QtCore/QString>
|
2009-11-02 19:52:28 +01:00
|
|
|
#include <QtCore/QDebug>
|
2009-09-15 13:03:13 +03:00
|
|
|
|
|
|
|
|
using namespace Mercurial::Internal;
|
|
|
|
|
using namespace Mercurial;
|
|
|
|
|
|
2009-11-03 14:21:48 +01:00
|
|
|
HgTask::HgTask(const QString &repositoryRoot, const QStringList &arguments, bool emitRaw)
|
2009-09-15 13:03:13 +03:00
|
|
|
: m_repositoryRoot(repositoryRoot),
|
|
|
|
|
arguments(arguments),
|
|
|
|
|
emitRaw(emitRaw),
|
|
|
|
|
editor(0)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2009-11-03 14:21:48 +01:00
|
|
|
HgTask::HgTask(const QString &repositoryRoot, const QStringList &arguments, VCSBase::VCSBaseEditor *editor)
|
2009-09-15 13:03:13 +03:00
|
|
|
: m_repositoryRoot(repositoryRoot),
|
|
|
|
|
arguments(arguments),
|
|
|
|
|
emitRaw(false),
|
|
|
|
|
editor(editor)
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2009-11-03 14:21:48 +01:00
|
|
|
MercurialJobRunner::MercurialJobRunner() :
|
|
|
|
|
plugin(MercurialPlugin::instance()),
|
|
|
|
|
keepRunning(true)
|
2009-09-15 13:03:13 +03:00
|
|
|
{
|
2009-11-03 14:21:48 +01:00
|
|
|
VCSBase::VCSBaseOutputWindow *ow = VCSBase::VCSBaseOutputWindow::instance();
|
|
|
|
|
connect(this, SIGNAL(error(QString)), ow, SLOT(appendError(QString)), Qt::QueuedConnection);
|
|
|
|
|
connect(this, SIGNAL(commandStarted(QString)), ow, SLOT(appendCommand(QString)), Qt::QueuedConnection);
|
2009-09-15 13:03:13 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MercurialJobRunner::~MercurialJobRunner()
|
|
|
|
|
{
|
|
|
|
|
stop();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MercurialJobRunner::stop()
|
|
|
|
|
{
|
|
|
|
|
mutex.lock();
|
|
|
|
|
keepRunning = false;
|
|
|
|
|
//Create a dummy task to break the cycle
|
|
|
|
|
QSharedPointer<HgTask> job(0);
|
|
|
|
|
jobs.enqueue(job);
|
|
|
|
|
waiter.wakeAll();
|
|
|
|
|
mutex.unlock();
|
|
|
|
|
|
|
|
|
|
wait();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MercurialJobRunner::restart()
|
|
|
|
|
{
|
|
|
|
|
stop();
|
|
|
|
|
mutex.lock();
|
|
|
|
|
keepRunning = true;
|
|
|
|
|
mutex.unlock();
|
|
|
|
|
start();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MercurialJobRunner::getSettings()
|
|
|
|
|
{
|
2009-11-03 16:38:39 +01:00
|
|
|
const MercurialSettings *settings = MercurialPlugin::instance()->settings();
|
2009-09-15 13:03:13 +03:00
|
|
|
binary = settings->binary();
|
|
|
|
|
timeout = settings->timeout();
|
|
|
|
|
standardArguments = settings->standardArguments();
|
|
|
|
|
}
|
|
|
|
|
|
2009-11-03 14:21:48 +01:00
|
|
|
void MercurialJobRunner::enqueueJob(const QSharedPointer<HgTask> &job)
|
2009-09-15 13:03:13 +03:00
|
|
|
{
|
|
|
|
|
mutex.lock();
|
|
|
|
|
jobs.enqueue(job);
|
|
|
|
|
waiter.wakeAll();
|
|
|
|
|
mutex.unlock();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MercurialJobRunner::run()
|
|
|
|
|
{
|
|
|
|
|
getSettings();
|
|
|
|
|
forever {
|
|
|
|
|
mutex.lock();
|
|
|
|
|
while (jobs.count() == 0)
|
|
|
|
|
waiter.wait(&mutex);
|
|
|
|
|
|
|
|
|
|
if (!keepRunning) {
|
|
|
|
|
jobs.clear();
|
|
|
|
|
mutex.unlock();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QSharedPointer<HgTask> job = jobs.dequeue();
|
|
|
|
|
mutex.unlock();
|
|
|
|
|
|
|
|
|
|
task(job);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-11-05 12:45:02 +01:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString MercurialJobRunner::msgTimeout(int timeoutMS)
|
|
|
|
|
{
|
|
|
|
|
return tr("Timed out after %1ms waiting for mercurial process to finish.").arg(timeoutMS);
|
|
|
|
|
}
|
|
|
|
|
|
2009-11-03 14:21:48 +01:00
|
|
|
void MercurialJobRunner::task(const QSharedPointer<HgTask> &job)
|
2009-09-15 13:03:13 +03:00
|
|
|
{
|
|
|
|
|
HgTask *taskData = job.data();
|
|
|
|
|
|
2009-11-03 14:21:48 +01:00
|
|
|
VCSBase::VCSBaseOutputWindow *outputWindow = VCSBase::VCSBaseOutputWindow::instance();
|
|
|
|
|
|
|
|
|
|
if (taskData->shouldEmit()) {
|
2009-09-15 13:03:13 +03:00
|
|
|
//Call the job's signal so the Initator of the job can process the data
|
|
|
|
|
//Because the QSharedPointer that holds the HgTask will go out of scope and hence be deleted
|
|
|
|
|
//we have to block and wait until the signal is delivered
|
2009-11-03 14:21:48 +01:00
|
|
|
connect(this, SIGNAL(output(QByteArray)), taskData, SIGNAL(rawData(QByteArray)),
|
2009-09-15 13:03:13 +03:00
|
|
|
Qt::BlockingQueuedConnection);
|
2009-11-03 14:21:48 +01:00
|
|
|
} else if (taskData->displayEditor()) {
|
2009-09-15 13:03:13 +03:00
|
|
|
//An editor has been created to display the data so send it there
|
2009-11-03 14:21:48 +01:00
|
|
|
connect(this, SIGNAL(output(QByteArray)),
|
|
|
|
|
taskData->displayEditor(), SLOT(setPlainTextData(QByteArray)),
|
|
|
|
|
Qt::QueuedConnection);
|
|
|
|
|
} else {
|
2009-09-15 13:03:13 +03:00
|
|
|
//Just output the data to the Mercurial output window
|
2009-11-03 14:21:48 +01:00
|
|
|
connect(this, SIGNAL(output(QByteArray)), outputWindow, SLOT(appendData(QByteArray)),
|
|
|
|
|
Qt::QueuedConnection);
|
|
|
|
|
}
|
2009-09-15 13:03:13 +03:00
|
|
|
|
2009-11-05 12:45:02 +01:00
|
|
|
const QStringList args = standardArguments + taskData->args();
|
|
|
|
|
emit commandStarted(msgExecute(binary, args));
|
2009-09-15 13:03:13 +03:00
|
|
|
//infom the user of what we are going to try and perform
|
|
|
|
|
|
|
|
|
|
if (Constants::debug)
|
|
|
|
|
qDebug() << Q_FUNC_INFO << "Repository root is " << taskData->repositoryRoot();
|
|
|
|
|
|
|
|
|
|
QProcess hgProcess;
|
|
|
|
|
hgProcess.setWorkingDirectory(taskData->repositoryRoot());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
hgProcess.start(binary, args);
|
|
|
|
|
|
2009-11-05 12:45:02 +01:00
|
|
|
if (!hgProcess.waitForStarted()) {
|
|
|
|
|
emit error(msgStartFailed(binary, hgProcess.errorString()));
|
2009-09-15 13:03:13 +03:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hgProcess.closeWriteChannel();
|
|
|
|
|
|
|
|
|
|
if (!hgProcess.waitForFinished(timeout)) {
|
|
|
|
|
hgProcess.terminate();
|
2009-11-05 12:45:02 +01:00
|
|
|
emit error(msgTimeout(timeout));
|
2009-09-15 13:03:13 +03:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((hgProcess.exitStatus() == QProcess::NormalExit) && (hgProcess.exitCode() == 0)) {
|
2009-11-03 14:21:48 +01:00
|
|
|
QByteArray stdOutput= hgProcess.readAllStandardOutput();
|
2009-09-15 13:03:13 +03:00
|
|
|
/*
|
|
|
|
|
* 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
|
|
|
|
|
* from revision specified
|
|
|
|
|
*/
|
2009-11-03 14:21:48 +01:00
|
|
|
if (stdOutput.isEmpty())
|
|
|
|
|
stdOutput = hgProcess.readAllStandardError();
|
|
|
|
|
emit output(stdOutput);
|
2009-09-15 13:03:13 +03:00
|
|
|
} else {
|
2009-11-03 14:21:48 +01:00
|
|
|
emit error(QString::fromLocal8Bit(hgProcess.readAllStandardError()));
|
2009-09-15 13:03:13 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
2009-11-03 14:21:48 +01:00
|
|
|
disconnect(this, SIGNAL(output(QByteArray)), 0, 0);
|
2009-09-15 13:03:13 +03:00
|
|
|
}
|