# # # add_file "src/monotone/MonotoneManager.cpp" # content [108727a80195ee0d80a68dc4aecacc5537904f66] # # add_file "src/monotone/MonotoneManager.h" # content [9298b06cfcb42318c73a0b32148ec744a0d2b18f] # # patch "src/monotone/MonotoneThread.cpp" # from [addea202657bdd3faaa5ccc4abe2e84ee3192a22] # to [d1c6c3dddc22a0bf421a81241db596ecd7ab2cde] # # patch "src/monotone/MonotoneThread.h" # from [cc5aac5ed9e210bace10ad2239cdaf71732bf26f] # to [0d826631d00d340b7d1b2bc60acc27a78b517827] # # patch "src/monotone/MonotoneUtil.cpp" # from [2ea8ebeb32be9829d6d27766f6d63ce6c3601dec] # to [2e09b05ab3d7c299d0b39655254326030cd6511f] # # patch "src/monotone/MonotoneUtil.h" # from [f04aee16fa423dddcabb3d91d909968785319f3c] # to [8577a38758622b8248682a538b53388640cf382a] # ============================================================ --- src/monotone/MonotoneManager.cpp 108727a80195ee0d80a68dc4aecacc5537904f66 +++ src/monotone/MonotoneManager.cpp 108727a80195ee0d80a68dc4aecacc5537904f66 @@ -0,0 +1,249 @@ +/*************************************************************************** + * Copyright (C) 2007 by Thomas Keller * + * address@hidden * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "MonotoneManager.h" +#include "vocab.h" +#include "BasicIOParser.h" + +// including this version +const QString MonotoneManager::MinInterfaceVersion = "5.0"; +// excluding this version +const QString MonotoneManager::MaxInterfaceVersion = "6.0"; + +// FIXME: I think we need to care somehow if we pass +// threads around - i.e. use shared ptrs or something +MonotoneThread * MonotoneManager::getThreadForWorkspace(const WorkspacePath & workspace) +{ + WorkspacePath normalizedWorkspace = normalizeWorkspacePath(workspace); + DatabaseFile databaseFile = getDatabaseFilePath(normalizedWorkspace); + return getThread(databaseFile, workspace); +} + +MonotoneThread * MonotoneManager::getThreadForDatabase(const DatabaseFile & database) +{ + return getThread(database, QString()); +} + +MonotoneThread * MonotoneManager::getThread(const DatabaseFile & database, const WorkspacePath & workspace) +{ + // FIXME: since we cannot set the workspace directory after we've + // started the process, we need to ensure that each workspace runs with + // its own process + QString ident = database + "|" + workspace; + if (!threadMap.contains(ident)) + { + // TODO: connect to started(int) and aborted(int, ...) here + // and wait for started() + MonotoneThread * thread = + new MonotoneThread(threadNumber++, mtnPath, database, workspace); + threadMap.insert(ident, thread); + } + + // TODO: we may want to add support for multiple threads for one + // and the same database here in the future... + MonotoneThread * thread = threadMap.value(ident); + if (!thread->isRunning()) + { + // TODO: disconnect from started(int) and aborted(int, ...) here + delete thread; + threadMap.remove(ident); + // up to the next round + return getThread(database, workspace); + } + return thread; +} + +WorkspacePath MonotoneManager::normalizeWorkspacePath(const WorkspacePath & workspace) +{ + QDir tempDir(workspace); + if (!tempDir.exists()) + { + throw GuitoneException(tr("workspace directory does not exist")); + } + + bool found = false; + WorkspacePath normalizedWorkspace; + do + { + if (tempDir.cd("_MTN")) + { + tempDir.cdUp(); + normalizedWorkspace = tempDir.absolutePath(); + found = true; + break; + } + } + while (!tempDir.isRoot() && tempDir.cdUp()); + + if (!found) + { + throw GuitoneException(tr("could not find _MTN directory")); + } + + return normalizedWorkspace; +} + +// we assume that the workspace was already normalized here +DatabaseFile MonotoneManager::getDatabaseFilePath(const WorkspacePath & workspace) +{ + // now check again if we know it + if (workspaceMap.contains(workspace)) + { + return workspaceMap.value(workspace); + } + + QFile optionsFile(workspace + "/_MTN/options"); + if (!optionsFile.open(QIODevice::ReadOnly | QIODevice::Text)) + { + throw GuitoneException(tr("could not open _MTN/options for reading")); + } + + QByteArray contents = optionsFile.readAll(); + optionsFile.close(); + + if (contents.size() == 0) + { + throw GuitoneException(tr("file _MTN/options is empty")); + } + + BasicIOParser parser(QString::fromUtf8(contents)); + if (!parser.parse()) + { + throw GuitoneException(tr("could not parse basic_io from _MTN/options")); + } + StanzaList stanzas = parser.getStanzas(); + I(stanzas.size() == 1); + Stanza st = stanzas.at(0); + + DatabaseFile databaseFilePath; + foreach (StanzaEntry entry, st) + { + if (entry.sym == "database") + { + I(entry.vals.size() == 1); + databaseFilePath = entry.vals.at(0); + break; + } + } + + if (databaseFilePath.isEmpty()) + { + throw GuitoneException(tr("could not find database for workspace")); + } + + // remember what we've just found for later requests + workspaceMap.insert(workspace, databaseFilePath); + return databaseFilePath; +} + +//! \todo many-to-one resolution +void MonotoneManager::aborted(int threadNumber, QProcess::ProcessError error, const QString & message) +{ + Q_UNUSED(threadNumber); + Q_UNUSED(error); + Q_UNUSED(message); +} + +bool MonotoneManager::singleRun(const QStringList & params, QByteArray & output) +{ + QProcess proc; + proc.start(mtnPath, params); + + // could not be started (invalid path, not executable, ...) + if (!proc.waitForStarted(5000)) + { + C(QString("`mtn %1` not started").arg(params.join(" "))); + return false; + } + + // process doesn't return, etc... + if (!proc.waitForFinished(5000)) + { + C(QString("`mtn %1` not finished").arg(params.join(" "))); + return false; + } + + // read all of the output + output.append(proc.readAll()); + return true; +} + +bool MonotoneManager::checkInterfaceVersion() +{ + QByteArray output; + QStringList opts; + + if (!singleRun(QStringList() << "automate" << "interface_version", output)) + { + C("Couldn't execute `automate interface_version`"); + return false; + } + + QRegExp regex("^(\\d+(?:\\.\\d+))"); + QString versionString = QString::fromUtf8(output); + + if (regex.indexIn(versionString) == -1) + { + D(QString("couldn't parse output: %1").arg(versionString)); + return false; + } + + QString curVersion = regex.cap(1); + + D(QString("interface version: %1, need one between ['%2' .. '%3'[") + .arg(curVersion) + .arg(MinInterfaceVersion) + .arg(MaxInterfaceVersion) + ); + + return versionCompare(curVersion, MinInterfaceVersion) >= 0 && + versionCompare(curVersion, MaxInterfaceVersion) < 0; +} + +/*! + Returns + -1 if right is greater + 0 if both are equal + 1 if left is greater + \sa checkInterfaceVersion() +*/ +int MonotoneManager::versionCompare(const QString & left, const QString & right) +{ + QStringList leftParts = left.split("."); + QStringList rightParts = right.split("."); + + int leftCount = leftParts.size(); + int rightCount = rightParts.size(); + int maxCount = leftCount > rightCount ? leftCount : rightCount; + + for (int i=0, j=maxCount; i r) return 1; + return -1; + } + + return 0; +} ============================================================ --- src/monotone/MonotoneManager.h 9298b06cfcb42318c73a0b32148ec744a0d2b18f +++ src/monotone/MonotoneManager.h 9298b06cfcb42318c73a0b32148ec744a0d2b18f @@ -0,0 +1,69 @@ +/*************************************************************************** + * Copyright (C) 2007 by Thomas Keller * + * address@hidden * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef MONOTONE_MANAGER_H +#define MONOTONE_MANAGER_H + +#include "MonotoneThread.h" + +class MonotoneManager : public QObject +{ + Q_OBJECT +public: + MonotoneManager(const QString & p) : mtnPath(p), threadNumber(0) {}; + ~MonotoneManager() {}; + + //! set the path to the monotone binary to use for all threads + inline void setMtnBinaryPath(const QString & path) { mtnPath = path; } + + //! returns the database filepath for a given workspace path + DatabaseFile getDatabaseFilePath(const WorkspacePath &); + + //! returns an appropriate MonotoneThread for the given workspace + MonotoneThread * getThreadForWorkspace(const WorkspacePath &); + + //! returns an appropriate MonotoneThread for the given database + MonotoneThread * getThreadForDatabase(const DatabaseFile &); + + //! runs a single, non-automate monotone command + bool singleRun(const QStringList &, QByteArray &); + + //! returns true if the loaded mtn binary suffices the version requirements + bool checkInterfaceVersion(); + + static const QString MinInterfaceVersion; + static const QString MaxInterfaceVersion; + +private: + MonotoneThread * getThread(const DatabaseFile &, const WorkspacePath &); + WorkspacePath normalizeWorkspacePath(const WorkspacePath &); + int versionCompare(const QString &, const QString &); + + QMap threadMap; + QMap workspaceMap; + QString mtnPath; + int threadNumber; + +private slots: + void aborted(int, QProcess::ProcessError, const QString &); +}; + +#endif + ============================================================ --- src/monotone/MonotoneThread.cpp addea202657bdd3faaa5ccc4abe2e84ee3192a22 +++ src/monotone/MonotoneThread.cpp d1c6c3dddc22a0bf421a81241db596ecd7ab2cde @@ -276,134 +276,3 @@ void MonotoneThread::abort() doAbort = true; } -// FIXME: I think we need to care somehow if we pass -// threads around - i.e. use shared ptrs or something -MonotoneThread * MonotoneThreadManager::getThreadForWorkspace(const WorkspacePath & workspace) -{ - WorkspacePath normalizedWorkspace = normalizeWorkspacePath(workspace); - DatabaseFile databaseFile = getDatabaseFilePath(normalizedWorkspace); - return getThread(databaseFile, workspace); -} - -MonotoneThread * MonotoneThreadManager::getThreadForDatabase(const DatabaseFile & database) -{ - return getThread(database, QString()); -} - -MonotoneThread * MonotoneThreadManager::getThread(const DatabaseFile & database, const WorkspacePath & workspace) -{ - // FIXME: since we cannot set the workspace directory after we've - // started the process, we need to ensure that each workspace runs with - // its own process - QString ident = database + "|" + workspace; - if (!threadMap.contains(ident)) - { - // TODO: connect to started(int) and aborted(int, ...) here - // and wait for started() - MonotoneThread * thread = - new MonotoneThread(threadNumber++, mtnPath, database, workspace); - threadMap.insert(ident, thread); - } - - // TODO: we may want to add support for multiple threads for one - // and the same database here in the future... - MonotoneThread * thread = threadMap.value(ident); - if (!thread->isRunning()) - { - // TODO: disconnect from started(int) and aborted(int, ...) here - delete thread; - threadMap.remove(ident); - // up to the next round - return getThread(database, workspace); - } - return thread; -} - -WorkspacePath MonotoneThreadManager::normalizeWorkspacePath(const WorkspacePath & workspace) -{ - QDir tempDir(workspace); - if (!tempDir.exists()) - { - throw GuitoneException(tr("workspace directory does not exist")); - } - - bool found = false; - WorkspacePath normalizedWorkspace; - do - { - if (tempDir.cd("_MTN")) - { - tempDir.cdUp(); - normalizedWorkspace = tempDir.absolutePath(); - found = true; - break; - } - } - while (!tempDir.isRoot() && tempDir.cdUp()); - - if (!found) - { - throw GuitoneException(tr("could not find _MTN directory")); - } - - return normalizedWorkspace; -} - -// we assume that the workspace was already normalized here -DatabaseFile MonotoneThreadManager::getDatabaseFilePath(const WorkspacePath & workspace) -{ - // now check again if we know it - if (workspaceMap.contains(workspace)) - { - return workspaceMap.value(workspace); - } - - QFile optionsFile(workspace + "/_MTN/options"); - if (!optionsFile.open(QIODevice::ReadOnly | QIODevice::Text)) - { - throw GuitoneException(tr("could not open _MTN/options for reading")); - } - - QByteArray contents = optionsFile.readAll(); - optionsFile.close(); - - if (contents.size() == 0) - { - throw GuitoneException(tr("file _MTN/options is empty")); - } - - BasicIOParser parser(QString::fromUtf8(contents)); - if (!parser.parse()) - { - throw GuitoneException(tr("could not parse basic_io from _MTN/options")); - } - StanzaList stanzas = parser.getStanzas(); - I(stanzas.size() == 1); - Stanza st = stanzas.at(0); - - DatabaseFile databaseFilePath; - foreach (StanzaEntry entry, st) - { - if (entry.sym == "database") - { - I(entry.vals.size() == 1); - databaseFilePath = entry.vals.at(0); - break; - } - } - - if (databaseFilePath.isEmpty()) - { - throw GuitoneException(tr("could not find database for workspace")); - } - - // remember what we've just found for later requests - workspaceMap.insert(workspace, databaseFilePath); - return databaseFilePath; -} - -void MonotoneThreadManager::aborted(int threadNumber, QProcess::ProcessError error, const QString & message) -{ - // FIXME: many-to-one resolution -} - ============================================================ --- src/monotone/MonotoneThread.h cc5aac5ed9e210bace10ad2239cdaf71732bf26f +++ src/monotone/MonotoneThread.h 0d826631d00d340b7d1b2bc60acc27a78b517827 @@ -156,36 +156,4 @@ private: QMutex lock; }; -class MonotoneThreadManager : public QObject -{ - Q_OBJECT - -public: - MonotoneThreadManager(const QString & p) : mtnPath(p), threadNumber(0) {}; - ~MonotoneThreadManager() {}; - - //! set the path to the monotone binary to use for all threads - inline void setMtnBinaryPath(const QString & path) { mtnPath = path; } - - //! returns the database filepath for a given workspace path - DatabaseFile getDatabaseFilePath(const WorkspacePath &); - - //! returns an appropriate MonotoneThread for the given workspace - MonotoneThread * getThreadForWorkspace(const WorkspacePath &); - - //! returns an appropriate MonotoneThread for the given database - MonotoneThread * getThreadForDatabase(const DatabaseFile &); - -private: - MonotoneThread * getThread(const DatabaseFile &, const WorkspacePath &); - WorkspacePath normalizeWorkspacePath(const WorkspacePath &); - - QMap threadMap; - QMap workspaceMap; - QString mtnPath; - int threadNumber; - -private slots: - void aborted(int, QProcess::ProcessError, const QString &); -}; #endif ============================================================ --- src/monotone/MonotoneUtil.cpp 2ea8ebeb32be9829d6d27766f6d63ce6c3601dec +++ src/monotone/MonotoneUtil.cpp 2e09b05ab3d7c299d0b39655254326030cd6511f @@ -22,11 +22,6 @@ #include "Guitone.h" #include "BasicIOParser.h" -// including this version -const QString MonotoneUtil::MinInterfaceVersion = "5.0"; -// excluding this version -const QString MonotoneUtil::MaxInterfaceVersion = "6.0"; - MonotoneUtil::MonotoneUtil(MonotoneThread * thread, const MonotoneTask & in) { connect( @@ -61,112 +56,10 @@ void MonotoneUtil::taskAborted(const Mon eventLoop.quit(); } -bool MonotoneUtil::runCommand(const QString & path, const QStringList & params, QString & output) -{ - QProcess proc; - proc.start(path, params); +// +// static methods +// - // could not be started (invalid path, not executable, ...) - if (!proc.waitForStarted(5000)) - { - C("process not started"); - return false; - } - - // process doesn't return, etc... - if (!proc.waitForFinished(5000)) - { - C("process not finished"); - return false; - } - - // read all of the output - output.append(proc.readAll()); - return true; -} - -bool MonotoneUtil::checkInterfaceVersion(const QString & path) -{ - QString output; - QStringList opts; - opts << "automate" << "interface_version"; - - if (!runCommand(path, opts, output)) - { - return false; - } - - QRegExp regex("^(\\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("interface version: %1, need one between ['%2' .. '%3'[") - .arg(curVersion) - .arg(MinInterfaceVersion) - .arg(MaxInterfaceVersion) - ); - - return versionCompare(curVersion, MinInterfaceVersion) >= 0 && - versionCompare(curVersion, MaxInterfaceVersion) < 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(); i rightCount ? leftCount : rightCount; - - for (int i=0, j=maxCount; i r) return 1; - return -1; - } - - return 0; -} - MonotoneTask MonotoneUtil::runSynchronousWorkspaceTask(const WorkspacePath & workspace, const MonotoneTask & task) { MonotoneThread * thread = APP->manager()->getThreadForWorkspace(workspace); @@ -458,3 +351,26 @@ QString MonotoneUtil::getFileId(const Da return data; } +// 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(); i