# # # rename "src/monotone/MonotoneDelegate.cpp" # to "src/monotone/MonotoneUtil.cpp" # # rename "src/monotone/MonotoneDelegate.h" # to "src/monotone/MonotoneUtil.h" # # patch "src/monotone/MonotoneThread.cpp" # from [05706d777769f38f7728955b17d4e227c6e1b711] # to [dce2c0128306f3c8316e73bdee982dd121571760] # # patch "src/monotone/MonotoneThread.h" # from [e11f5a4d15e376cb9a39fa268e1facb8e39bef67] # to [38117b009f3e7a519cce82f7e725ecf3b19d8ce4] # # patch "src/monotone/MonotoneUtil.cpp" # from [0b1b0ebfaa468cbf89d3ef32a54e9791405b86f5] # to [15599cf0fae9d75b255f3ed9ebe5bccb092cfe44] # # patch "src/monotone/MonotoneUtil.h" # from [6971e5c0d79d50fc766f84fb395cf648da80c8e5] # to [d165306a429a2f07f967e554f5ed94fa5e147576] # ============================================================ --- src/monotone/MonotoneThread.cpp 05706d777769f38f7728955b17d4e227c6e1b711 +++ src/monotone/MonotoneThread.cpp dce2c0128306f3c8316e73bdee982dd121571760 @@ -281,13 +281,23 @@ MonotoneThread * MonotoneThreadManager:: { if (!threadMap.contains(database)) { + // TODO: connect to taskAborted here MonotoneThread * thread = new MonotoneThread(mtnPath, database, workspace); threadMap.insert(database, thread); } - // FIXME: we may want to add support for multiple threads for one + // TODO: we may want to add support for multiple threads for one // and the same database here in the future... - return threadMap.value(database); + MonotoneThread * thread = threadMap.value(database); + if (!thread->isRunning()) + { + // TODO: disconnect from taskAborted here + delete thread; + threadMap.remove(database); + // up to the next round + return getThread(database, workspace); + } + return thread; } QString MonotoneThreadManager::normalizeWorkspacePath(const QString & workspace) @@ -373,3 +383,13 @@ QString MonotoneThreadManager::getDataba return databaseFilePath; } +void MonotoneThreadManager::aborted(QProcess::ProcessError error, const QString & message) +{ + // FIXME: many-to-one resolution +} + +void MonotoneThreadManager::taskAborted(const MonotoneTask & task) +{ + // FIXME: many-to-one resolution +} + ============================================================ --- src/monotone/MonotoneThread.h e11f5a4d15e376cb9a39fa268e1facb8e39bef67 +++ src/monotone/MonotoneThread.h 38117b009f3e7a519cce82f7e725ecf3b19d8ce4 @@ -48,7 +48,7 @@ public: ByteArrayList getOptions() const { return options; } QByteArray getOutput() const { return output; } QString getOutputUtf8() const { return QString::fromUtf8(output); } - bool getReturnCode() const { return returnCode; } + int getReturnCode() const { return returnCode; } int getCommandNumber() const { return commandNumber; } private: @@ -108,6 +108,7 @@ public: MonotoneThreadManager(const QString & p) : mtnPath(p) {}; ~MonotoneThreadManager() {}; + inline void setMtnBinaryPath(const QString & path) { mtnPath = path; } MonotoneThread * getThread(const QString &); MonotoneThread * getThread(const QString &, const QString &); @@ -118,5 +119,9 @@ private: QString getDatabaseFilePath(const QString &); QString normalizeWorkspacePath(const QString &); + +private slots: + void aborted(QProcess::ProcessError, const QString &); + void taskAborted(const MonotoneTask &); }; #endif ============================================================ --- src/monotone/MonotoneDelegate.cpp 0b1b0ebfaa468cbf89d3ef32a54e9791405b86f5 +++ src/monotone/MonotoneUtil.cpp 15599cf0fae9d75b255f3ed9ebe5bccb092cfe44 @@ -18,109 +18,182 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ -#include "MonotoneDelegate.h" -#include "Monotone.h" +#include "MonotoneUtil.h" #include "Guitone.h" #include "BasicIOParser.h" -MonotoneDelegate::MonotoneDelegate(AutomateCommand * cmd) - : cmdModel(cmd) +MonotoneUtil::MonotoneUtil(MonotoneThread * thread, const MonotoneTask & in) { + connect( + thread, SIGNAL(taskFinished(const MonotoneTask &)), + this, SLOT(taskFinished(const MonotoneTask &)) + ); + commandNumber = thread->enqueueTask(in); } -MonotoneDelegate::~MonotoneDelegate() +MonotoneTask & MonotoneUtil::getTaskResult() { - int cnt = commandNumbers.size(); - if (cnt > 0) - { - W(QString("%1 pending commands").arg(cnt)); - APP->restoreOverrideCursor(); - } + eventLoop.exec(); + return result; } -bool MonotoneDelegate::triggerCommand(const QStringList & cmd) +void MonotoneUtil::taskFinished(const MonotoneTask & task) { - return triggerCommand(cmd, QStringList()); + if (task.getCommandNumber() != commandNumber) return; + result = task; + eventLoop.quit(); } -bool MonotoneDelegate::triggerCommand(const QStringList & cmd, const QStringList & opts) +bool MonotoneUtil::runCommand(const QString & path, const QStringList & params, QString & output) { - QObject * obj = dynamic_cast(cmdModel); - I(obj); - Monotone * mtn = MTN(obj); - - int cmdNum; - if (mtn->triggerCommand(cmd, opts, cmdNum)) + QProcess proc; + proc.start(path, params); + + // could not be started (invalid path, not executable, ...) + if (!proc.waitForStarted(5000)) { - if (commandNumbers.size() == 0) - { - APP->setOverrideCursor(Qt::WaitCursor); - - connect( - mtn, SIGNAL(commandFinished(int)), - this, SLOT(commandFinished(int)) - ); - } - - commandNumbers.insert(cmdNum); - - return true; + D("process not started"); + return false; } - return false; + // process doesn't return, etc... + if (!proc.waitForFinished(5000)) + { + D("process not finished"); + return false; + } + + // read all of the output + output.append(proc.readAll()); + return true; } + +bool MonotoneUtil::checkProgramVersion(const QString & path) +{ + QString output; + QStringList opts; + opts << "--version"; + + if (!runCommand(path, opts, output)) + { + return false; + } + + QRegExp regex("^monotone (\\d+(?:\\.\\d+))"); + + if (regex.indexIn(output) == -1) + { + D(QString("couldn't parse output: %1").arg(output)); + return false; + } + + QString curVersion = regex.cap(1); + + D(QString("monotone version: %1, need: %2") + .arg(curVersion) + .arg(REQ_MTN_VERSION) + ); + + return versionCompare(curVersion, REQ_MTN_VERSION) >= 0; +} -// FIXME: if a request is aborted, commandFinished is never signalled, -// do we need to take care of that here? -void MonotoneDelegate::commandFinished(int cmdNum) +bool MonotoneUtil::checkInterfaceVersion(const QString & path) { - if (!commandNumbers.contains(cmdNum)) return; + QString output; + QStringList opts; + opts << "automate" << "interface_version"; - QObject * obj = dynamic_cast(cmdModel); - I(obj); - Monotone * mtn = MTN(obj); + if (!runCommand(path, opts, output)) + { + return false; + } - // FIXME: does any of our models ever need the raw data? - cmdModel->setAutomateData(mtn->getDecodedData(cmdNum)); - int returnCode = mtn->getReturnCode(cmdNum); - - if (returnCode == 0) + QRegExp regex("^(\\d+(?:\\.\\d+))"); + + if (regex.indexIn(output) == -1) { - cmdModel->parseOutput(); + D(QString("couldn't parse output: %1").arg(output)); + return false; } - else + + QString curVersion = regex.cap(1); + + D(QString("interface version: %1, need: %2") + .arg(curVersion) + .arg(REQ_MTN_INT_VERSION) + ); + + return versionCompare(curVersion, REQ_MTN_INT_VERSION) >= 0; +} + +// strip mtn: warning: and alike from error strings coming from mtn +// and unwrap them, if this fails we add the original string unwrapped +QString MonotoneUtil::stripMtnPrefix(const QString & input) +{ + QString output; + QStringList list = input.split(QRegExp("\\n")); + + // FIXME: we should actually use basename(mtnBinaryPath) in case + // the binary is renamed, but I'm too lazy for this right now... + // and this would also require to make this method non-static + QRegExp rx = QRegExp("^(?:mtn\\:[ ]+)?[^:]+\\:[ ]*(.+)"); + for (int i=0, j=list.size(); ihandleError(returnCode)) + if (rx.indexIn(list[i]) != -1) { - C(QString("Couldn't handle error %1").arg(returnCode)); + output += rx.cap(1) + " "; + continue; } + output += list[i] + " "; } + return output.trimmed(); +} + +// Returns +// -1 if right is greater +// 0 if both are equal +// 1 if left is greater +// FIXME: this should be moved somewhere else +int MonotoneUtil::versionCompare(const QString & left, const QString & right) +{ + QStringList leftParts = left.split("."); + QStringList rightParts = right.split("."); - commandNumbers.remove(cmdNum); + int leftCount = leftParts.size(); + int rightCount = rightParts.size(); + int maxCount = leftCount > rightCount ? leftCount : rightCount; - if (commandNumbers.size() == 0) + for (int i=0, j=maxCount; irestoreOverrideCursor(); + if (l == r) continue; + if (l > r) return 1; + return -1; } + + return 0; } -QString MonotoneDelegate::getBaseWorkspaceRevision(QObject * obj) +MonotoneTask MonotoneUtil::runSynchronousTask(const QString & db, const MonotoneTask & task) { - Monotone * mtn = MTN(obj); + MonotoneThread * thread = APP->manager()->getThread(db); + MonotoneUtil * instance = new MonotoneUtil(thread, task); + MonotoneTask result = instance->getTaskResult(); + delete instance; + return result; +} - int commandNumber; - bool ret = mtn->executeCommand( - QStringList() << "get_base_revision_id", commandNumber - ); +QString MonotoneUtil::getBaseWorkspaceRevision(const QString & db) +{ + MonotoneTask in(QStringList() << "get_base_revision_id"); + MonotoneTask out = runSynchronousTask(db, in); - QString data = mtn->getDecodedData(commandNumber); - - if (!ret || mtn->getReturnCode(commandNumber) > 0) + QString data = out.getOutputUtf8(); + if (out.getReturnCode() > 0) { C(QString("Could not execute get_base_revision_id: %1").arg(data)); return QString(); @@ -134,16 +207,13 @@ QString MonotoneDelegate::getBaseWorkspa return data; } -QString MonotoneDelegate::getOption(QObject * obj, const QString & opt) +QString MonotoneUtil::getOption(const QString & db, const QString & opt) { - Monotone * mtn = MTN(obj); + MonotoneTask in(QStringList() << "get_option" << opt); + MonotoneTask out = runSynchronousTask(db, in); - int cmdNum; - mtn->executeCommand(QStringList() << "get_option" << opt, cmdNum); - - QString data = mtn->getDecodedData(cmdNum); - - if (mtn->getReturnCode(cmdNum) > 0) + QString data = out.getOutputUtf8(); + if (out.getReturnCode() > 0) { C(QString("Couldn't retrieve option %1: %2").arg(opt).arg(data)); return QString(); @@ -155,9 +225,9 @@ QString MonotoneDelegate::getOption(QObj return data; } -QString MonotoneDelegate::getBranchName(QObject * obj, const QString & defaultBranch) +QString MonotoneUtil::getBranchName(const QString & db, const QString & defaultBranch) { - QString branchName = getOption(obj, "branch"); + QString branchName = getOption(db, "branch"); if (branchName.size() == 0) { @@ -166,11 +236,11 @@ QString MonotoneDelegate::getBranchName( return branchName; } -QString MonotoneDelegate::getBranchNameShort(QObject * obj) +QString MonotoneUtil::getBranchNameShort(const QString & db) { // FIXME: currently there is only support for the domain branch scheme // tld.domain.project.branch - QString branchName = getBranchName(obj); + QString branchName = getBranchName(db); QStringList parts = branchName.split('.'); if (parts.size() == 1) return branchName; @@ -197,16 +267,13 @@ QString MonotoneDelegate::getBranchNameS return shortBranchName; } -QStringList MonotoneDelegate::resolveSelector(QObject * obj, const QString & selector) +QStringList MonotoneUtil::resolveSelector(const QString & db, const QString & selector) { - Monotone * mtn = MTN(obj); + MonotoneTask in(QStringList() << "select" << selector); + MonotoneTask out = runSynchronousTask(db, in); - int cmdNum; - mtn->executeCommand(QStringList() << "select" << selector, cmdNum); - - QString data = mtn->getDecodedData(cmdNum); - - if (mtn->getReturnCode(cmdNum) > 0) + QString data = out.getOutputUtf8(); + if (out.getReturnCode() > 0) { C(QString("Couldn't resolve selector %1: %2").arg(selector).arg(data)); return QStringList(); @@ -216,30 +283,28 @@ QStringList MonotoneDelegate::resolveSel return revList; } -RevisionCerts MonotoneDelegate::getRevisionCerts(QObject * obj, const QString & revision) +RevisionCerts MonotoneUtil::getRevisionCerts(const QString & db, const QString & revision) { - Monotone * mtn = MTN(obj); + MonotoneTask in(QStringList() << "certs" << revision); + MonotoneTask out = runSynchronousTask(db, in); - int cmdNum; - mtn->executeCommand(QStringList() << "certs" << revision, cmdNum); - - QString data = mtn->getDecodedData(cmdNum); - RevisionCerts certs; - - if (mtn->getReturnCode(cmdNum) > 0) + QString data = out.getOutputUtf8(); + if (out.getReturnCode() > 0) { C(QString("Couldn't query revision certs for %1: %2").arg(revision).arg(data)); - return certs; + return RevisionCerts(); } BasicIOParser parser(data); if (!parser.parse()) { C("Could not parse basic_io."); - return certs; + return RevisionCerts(); } + RevisionCerts certs; StanzaList stanzas = parser.getStanzas(); + foreach (Stanza st, stanzas) { RevisionCert cert; @@ -262,51 +327,31 @@ RevisionCerts MonotoneDelegate::getRevis return certs; } -QString MonotoneDelegate::getDatabaseFilePath(QObject * obj) +FileEntryList MonotoneUtil::getRevisionManifest(const QString & db, const QString & revision) { - Monotone * mtn = MTN(obj); - QString path = mtn->getDatabaseFilePath(); - - // apparently a workspace is loaded... - if (path.isEmpty()) - { - path = getOption(obj, "database"); - } - - I(!path.isEmpty()); - return path; -} - -FileEntryList MonotoneDelegate::getRevisionManifest(QObject * obj, const QString & revision) -{ - Monotone * mtn = MTN(obj); - - QStringList cmd; - cmd << "get_manifest_of"; + QStringList cmd = QStringList() << "get_manifest_of"; if (!revision.isEmpty()) - { cmd << revision; - } - int cmdNum; - mtn->executeCommand(cmd, cmdNum); + MonotoneTask in(cmd); + MonotoneTask out = runSynchronousTask(db, in); - QString data = mtn->getDecodedData(cmdNum); - FileEntryList entries; - - if (mtn->getReturnCode(cmdNum) > 0) + QString data = out.getOutputUtf8(); + if (out.getReturnCode() > 0) { C(QString("Couldn't query manifest entries for %1: %2").arg(revision).arg(data)); - return entries; + return FileEntryList(); } BasicIOParser parser(data); if (!parser.parse()) { C("Could not parse basic_io."); - return entries; + return FileEntryList(); } + // FIXME: this should go into a separate function / class + FileEntryList entries; StanzaList stanzas = parser.getStanzas(); foreach (Stanza st, stanzas) { @@ -348,29 +393,26 @@ FileEntryList MonotoneDelegate::getRevis return entries; } -QStringList MonotoneDelegate::getPrivateKeyList(QObject * obj) +QStringList MonotoneUtil::getPrivateKeyList(const QString & db) { - Monotone * mtn = MTN(obj); + MonotoneTask in(QStringList() << "keys"); + MonotoneTask out = runSynchronousTask(db, in); - int cmdNum; - mtn->executeCommand(QStringList() << "keys", cmdNum); - - QString data = mtn->getDecodedData(cmdNum); - QStringList keys; - - if (mtn->getReturnCode(cmdNum) > 0) + QString data = out.getOutputUtf8(); + if (out.getReturnCode() > 0) { C(QString("Couldn't query keys: %1").arg(data)); - return keys; + return QStringList(); } BasicIOParser parser(data); if (!parser.parse()) { C("Could not parse basic_io."); - return keys; + return QStringList(); } + QStringList keys; StanzaList stanzas = parser.getStanzas(); foreach (Stanza st, stanzas) { @@ -394,16 +436,13 @@ QStringList MonotoneDelegate::getPrivate return keys; } -QString MonotoneDelegate::getFileId(QObject * obj, const QString & path) +QString MonotoneUtil::getFileId(const QString & db, const QString & path) { - Monotone * mtn = MTN(obj); + MonotoneTask in(QStringList() << "identify" << path); + MonotoneTask out = runSynchronousTask(db, in); - int cmdNum; - mtn->executeCommand(QStringList() << "identify" << path, cmdNum); - - QString data = mtn->getDecodedData(cmdNum); - - if (mtn->getReturnCode(cmdNum) > 0) + QString data = out.getOutputUtf8(); + if (out.getReturnCode() > 0) { C(QString("Couldn't identify path: %1").arg(data)); return QString(); ============================================================ --- src/monotone/MonotoneDelegate.h 6971e5c0d79d50fc766f84fb395cf648da80c8e5 +++ src/monotone/MonotoneUtil.h d165306a429a2f07f967e554f5ed94fa5e147576 @@ -18,43 +18,46 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ -#ifndef MONOTONE_DELEGATE_H -#define MONOTONE_DELEGATE_H +#ifndef MONOTONE_UTIL_H +#define MONOTONE_UTIL_H -#include "AutomateCommand.h" #include "vocab.h" +#include "MonotoneThread.h" -#include -#include +#include - -class MonotoneDelegate : public QObject +class MonotoneUtil : public QObject { Q_OBJECT - public: - MonotoneDelegate(AutomateCommand *); - ~MonotoneDelegate(); - bool triggerCommand(const QStringList &); - bool triggerCommand(const QStringList &, const QStringList &); + // various utility functions + static bool runCommand(const QString &, const QStringList &, QString &); + static bool checkProgramVersion(const QString &); + static bool checkInterfaceVersion(const QString &); + static int versionCompare(const QString &, const QString &); + static QString stripMtnPrefix(const QString &); - static QString getBaseWorkspaceRevision(QObject *); - static QString getOption(QObject *, const QString &); - static QString getBranchName(QObject *, const QString & defaultBranch = tr("[unknown branch]")); - static QString getBranchNameShort(QObject *); - static QStringList resolveSelector(QObject *, const QString &); - static RevisionCerts getRevisionCerts(QObject *, const QString &); - static QString getDatabaseFilePath(QObject *); - static FileEntryList getRevisionManifest(QObject *, const QString & rev = QString()); - static QStringList getPrivateKeyList(QObject *); - static QString getFileId(QObject *, const QString &); + // synchronous commands + static MonotoneTask runSynchronousTask(const QString &, const MonotoneTask &); + static QString getBaseWorkspaceRevision(const QString &); + static QString getOption(const QString &, const QString &); + static QString getBranchName(const QString &, const QString & defaultBranch = tr("[unknown branch]")); + static QString getBranchNameShort(const QString &); + static QStringList resolveSelector(const QString &, const QString &); + static RevisionCerts getRevisionCerts(const QString &, const QString &); + static FileEntryList getRevisionManifest(const QString &, const QString & rev = QString()); + static QStringList getPrivateKeyList(const QString &); + static QString getFileId(const QString &, const QString &); private: - AutomateCommand * cmdModel; - QSet commandNumbers; + MonotoneUtil(MonotoneThread *, const MonotoneTask &); + MonotoneTask & getTaskResult(); + QEventLoop eventLoop; + MonotoneTask result; + int commandNumber; private slots: - void commandFinished(int); + void taskFinished(const MonotoneTask &); }; #endif