commit c2f11c27139838fe3c7bf9b703243222dc1f504a
parent 4c6d2b8f5d9a245cf1fef1100fc62d3090d5ad56
Author: cfillion <cfillion@users.noreply.github.com>
Date: Thu, 3 Dec 2015 01:13:27 -0500
big refactoring of the installation process
Diffstat:
13 files changed, 292 insertions(+), 168 deletions(-)
diff --git a/src/config.cpp b/src/config.cpp
@@ -8,15 +8,15 @@
using namespace std;
-static const char *REPOS_GROUP = "repositories";
+static const char *REMOTES_GROUP = "remotes";
-static string RepoNameKey(const int i)
+static string RemoteNameKey(const int i)
{
static const string key = "name";
return key + to_string(i);
}
-static string RepoUrlKey(const int i)
+static string RemoteUrlKey(const int i)
{
static const string key = "url";
return key + to_string(i);
@@ -24,13 +24,9 @@ static string RepoUrlKey(const int i)
static const int URL_SIZE = 2083;
-Config::Config()
-{
-}
-
void Config::fillDefaults()
{
- m_repositories.push_back({"ReaScripts",
+ m_remotes.push_back({"ReaScripts",
"https://github.com/ReaTeam/ReaScripts/raw/master/index.xml"});
}
@@ -44,45 +40,45 @@ void Config::read(const Path &path)
return;
}
- readRepositories();
+ readRemotes();
}
void Config::write() const
{
- writeRepositories();
+ writeRemotes();
}
-void Config::readRepositories()
+void Config::readRemotes()
{
int i = 0;
do {
char name[URL_SIZE];
- GetPrivateProfileString(REPOS_GROUP, RepoNameKey(i).c_str(),
+ GetPrivateProfileString(REMOTES_GROUP, RemoteNameKey(i).c_str(),
"", name, sizeof(name), m_path.c_str());
char url[URL_SIZE];
- GetPrivateProfileString(REPOS_GROUP, RepoUrlKey(i).c_str(),
+ GetPrivateProfileString(REMOTES_GROUP, RemoteUrlKey(i).c_str(),
"", url, sizeof(url), m_path.c_str());
if(!strlen(name) || !strlen(url))
break;
- m_repositories.push_back({name, url});
+ m_remotes.push_back({name, url});
} while(++i);
}
-void Config::writeRepositories() const
+void Config::writeRemotes() const
{
- const int size = m_repositories.size();
+ const int size = m_remotes.size();
for(int i = 0; i < size; i++) {
- const Repository &repo = m_repositories[i];
+ const Remote &remote = m_remotes[i];
- WritePrivateProfileString(REPOS_GROUP, RepoNameKey(i).c_str(),
- repo.name().c_str(), m_path.c_str());
+ WritePrivateProfileString(REMOTES_GROUP, RemoteNameKey(i).c_str(),
+ remote.name().c_str(), m_path.c_str());
- WritePrivateProfileString(REPOS_GROUP, RepoUrlKey(i).c_str(),
- repo.url().c_str(), m_path.c_str());
+ WritePrivateProfileString(REMOTES_GROUP, RemoteUrlKey(i).c_str(),
+ remote.url().c_str(), m_path.c_str());
}
}
diff --git a/src/config.hpp b/src/config.hpp
@@ -2,43 +2,26 @@
#define REAPACK_CONFIG_HPP
#include <string>
-#include <vector>
-class Repository {
-public:
- Repository(const std::string &name, const std::string &url)
- : m_name(name), m_url(url)
- {}
-
- const std::string &name() const { return m_name; }
- const std::string &url() const { return m_url; }
-
-private:
- std::string m_name;
- std::string m_url;
-};
-
-typedef std::vector<Repository> RepositoryList;
+#include "remote.hpp"
class Path;
class Config {
public:
- Config();
-
void read(const Path &);
void write() const;
- const RepositoryList &repositories() const { return m_repositories; }
+ const RemoteList &remotes() const { return m_remotes; }
private:
void fillDefaults();
std::string m_path;
- void readRepositories();
- void writeRepositories() const;
- RepositoryList m_repositories;
+ void readRemotes();
+ void writeRemotes() const;
+ RemoteList m_remotes;
};
#endif
diff --git a/src/database.hpp b/src/database.hpp
@@ -9,7 +9,11 @@
class TiXmlElement;
+class Database;
+typedef std::vector<Database *> DatabaseList;
+
class Category;
+typedef std::vector<Category *> CategoryList;
class Database {
public:
@@ -21,17 +25,17 @@ public:
const std::string &name() const { return m_name; }
void addCategory(Category *cat);
- const std::vector<Category *> &categories() const { return m_categories; }
+ const CategoryList &categories() const { return m_categories; }
Category *category(const int i) const { return m_categories[i]; }
- const std::vector<Package *> &packages() const { return m_packages; }
+ const PackageList &packages() const { return m_packages; }
private:
static Database *loadV1(TiXmlElement *);
std::string m_name;
- std::vector<Category *> m_categories;
- std::vector<Package *> m_packages;
+ CategoryList m_categories;
+ PackageList m_packages;
};
class Category {
@@ -45,14 +49,14 @@ public:
Database *database() const { return m_database; }
void addPackage(Package *pack);
- const std::vector<Package *> &packages() const { return m_packages; }
+ const PackageList &packages() const { return m_packages; }
Package *package(const int i) const { return m_packages[i]; }
private:
Database *m_database;
std::string m_name;
- std::vector<Package *> m_packages;
+ PackageList m_packages;
};
#endif
diff --git a/src/download.cpp b/src/download.cpp
@@ -10,7 +10,7 @@ vector<Download *> Download::s_active;
static const int DOWNLOAD_TIMEOUT = 5;
-Download::Download(const std::string &name, const std::string &url)
+Download::Download(const string &name, const string &url)
: m_threadHandle(0), m_name(name), m_url(url)
{
reset();
@@ -37,15 +37,16 @@ void Download::reset()
m_contents.clear();
}
-void Download::addCallback(const DownloadCallback &callback)
+void Download::addCallback(const Download::Callback &callback)
{
- m_callback.push_back(callback);
+ m_onFinish.connect(callback);
}
void Download::TimerTick()
{
vector<Download *> &activeDownloads = Download::s_active;
const int size = activeDownloads.size();
+ const auto begin = activeDownloads.begin();
for(int i = 0; i < size; i++) {
Download *download = activeDownloads[i];
@@ -55,7 +56,7 @@ void Download::TimerTick()
// this need to be done before execCallback in case one of the callback
// deletes the download object
- activeDownloads.erase(activeDownloads.begin() + i);
+ activeDownloads.erase(begin + i);
download->execCallbacks();
}
@@ -178,8 +179,7 @@ void Download::execCallbacks()
m_threadHandle = 0;
}
- for(DownloadCallback callback : m_callback)
- callback(this);
+ m_onFinish();
}
bool Download::isFinished()
@@ -214,13 +214,9 @@ DownloadQueue::~DownloadQueue()
}
}
-void DownloadQueue::push(const string &name,
- const string &url, DownloadCallback cb)
+void DownloadQueue::push(Download *dl)
{
- Download *download = new Download(name, url);
- download->addCallback(cb);
-
- download->addCallback([=](Download *dl) {
+ dl->addCallback([=]() {
m_queue.pop();
delete dl;
@@ -228,8 +224,8 @@ void DownloadQueue::push(const string &name,
m_queue.front()->start();
});
- m_queue.push(download);
+ m_queue.push(dl);
if(m_queue.size() == 1)
- download->start();
+ dl->start();
}
diff --git a/src/download.hpp b/src/download.hpp
@@ -1,20 +1,20 @@
#ifndef REAPACK_DOWNLOAD_HPP
#define REAPACK_DOWNLOAD_HPP
-#include <functional>
#include <queue>
#include <string>
#include <vector>
+#include <boost/signals2.hpp>
#include <WDL/mutex.h>
#include <reaper_plugin.h>
-class Download;
-typedef std::function<void(Download *)> DownloadCallback;
-
class Download {
public:
+ typedef boost::signals2::signal<void ()> Signal;
+ typedef Signal::slot_type Callback;
+
Download(const std::string &name, const std::string &url);
~Download();
@@ -26,7 +26,7 @@ public:
bool isFinished();
bool isAborted();
- void addCallback(const DownloadCallback &);
+ void addCallback(const Callback &);
void start();
void stop();
@@ -51,7 +51,8 @@ private:
std::string m_name;
std::string m_url;
- std::vector<DownloadCallback> m_callback;
+
+ Signal m_onFinish;
WDL_Mutex m_mutex;
};
@@ -60,8 +61,9 @@ class DownloadQueue {
public:
~DownloadQueue();
- void push(const std::string &name, const std::string &url, DownloadCallback);
+ void push(Download *);
int size() const { return m_queue.size(); }
+ bool empty() const { return m_queue.empty(); }
private:
std::queue<Download *> m_queue;
diff --git a/src/main.cpp b/src/main.cpp
@@ -27,7 +27,7 @@ extern "C" REAPER_PLUGIN_DLL_EXPORT int REAPER_PLUGIN_ENTRYPOINT(
reapack.init(instance, rec);
reapack.setupAction("REAPACKSYNC", "ReaPack: Synchronize Packages",
- &reapack.action, std::bind(&ReaPack::synchronizeAll, reapack));
+ &reapack.action, std::bind(&ReaPack::synchronize, reapack));
rec->Register("hookcommand", (void *)commandHook);
diff --git a/src/package.hpp b/src/package.hpp
@@ -4,8 +4,11 @@
#include "path.hpp"
#include "version.hpp"
-class Category;
class Database;
+class Category;
+
+class Package;
+typedef std::vector<Package *> PackageList;
class Package {
public:
diff --git a/src/reapack.cpp b/src/reapack.cpp
@@ -1,13 +1,16 @@
#include "reapack.hpp"
-#include "errors.hpp"
-
-#include <fstream>
+#include "transaction.hpp"
#include <reaper_plugin_functions.h>
using namespace std;
+ReaPack::ReaPack()
+ : m_transaction(0)
+{
+}
+
void ReaPack::init(REAPER_PLUGIN_HINSTANCE instance, reaper_plugin_info_t *rec)
{
m_instance = instance;
@@ -16,10 +19,6 @@ void ReaPack::init(REAPER_PLUGIN_HINSTANCE instance, reaper_plugin_info_t *rec)
m_resourcePath.append(GetResourcePath());
m_config.read(m_resourcePath + "reapack.ini");
-
- m_dbPath = m_resourcePath + "ReaPack";
-
- RecursiveCreateDirectory(m_dbPath.join().c_str(), 0);
}
void ReaPack::cleanup()
@@ -49,95 +48,31 @@ bool ReaPack::execActions(const int id, const int)
return true;
}
-void ReaPack::synchronizeAll()
+void ReaPack::synchronize()
{
- RepositoryList repos = m_config.repositories();
-
- if(repos.empty()) {
- ShowMessageBox("No repository configured, nothing to do!", "ReaPack", 0);
+ if(m_transaction)
return;
- }
-
- for(const Repository &repo : repos) {
- try {
- synchronize(repo);
- }
- catch(const reapack_error &e) {
- ShowMessageBox(e.what(), repo.name().c_str(), 0);
- }
- }
-}
-
-void ReaPack::synchronize(const Repository &repo)
-{
- m_downloadQueue.push(repo.name(), repo.url(), [=](Download *dl) {
- if(dl->status() != 200) {
- ShowMessageBox(dl->contents().c_str(), repo.name().c_str(), 0);
- return;
- }
-
- const Path path = m_dbPath + (repo.name() + ".xml");
-
- ofstream file(path.join());
- if(file.bad()) {
- ShowMessageBox(strerror(errno), repo.name().c_str(), 0);
- return;
- }
- file << dl->contents();
- file.close();
+ RemoteList remotes = m_config.remotes();
- Database *db = Database::load(path.join().c_str());
- db->setName(repo.name());
-
- synchronize(db);
- });
-}
-
-void ReaPack::synchronize(Database *database)
-{
- if(database->packages().empty()) {
- ShowMessageBox("The package database is empty, nothing to do!",
+ if(remotes.empty()) {
+ ShowMessageBox("No remote repository configured, nothing to do!",
"ReaPack", 0);
- delete database;
-
return;
}
- for(Package *pkg : database->packages()) {
- try {
- installPackage(pkg);
- }
- catch(const reapack_error &e) {
- ShowMessageBox(e.what(), pkg->name().c_str(), 0);
- }
- }
-}
+ m_transaction = new Transaction(m_resourcePath);
-void ReaPack::installPackage(Package *pkg)
-{
- const std::string &url = pkg->lastVersion()->source(0)->url();
-
- m_downloadQueue.push(pkg->name(), url, [=](Download *dl) {
- if(m_downloadQueue.size() == 1)
- delete pkg->category()->database();
-
- if(dl->status() != 200) {
- ShowMessageBox(dl->contents().c_str(), dl->name().c_str(), 0);
- return;
- }
-
- const Path path = m_resourcePath + pkg->targetLocation();
- RecursiveCreateDirectory(path.dirname().c_str(), 0);
-
- ofstream file(path.join());
- if(file.bad()) {
- ShowMessageBox(strerror(errno), dl->name().c_str(), 0);
- return;
- }
+ m_transaction->onReady([=] {
+ // TODO: display the package list with the changelogs
+ m_transaction->run();
+ });
- file << dl->contents();
- file.close();
+ m_transaction->onFinish([=] {
+ delete m_transaction;
+ m_transaction = 0;
});
+
+ m_transaction->fetch(remotes);
}
diff --git a/src/reapack.hpp b/src/reapack.hpp
@@ -5,17 +5,20 @@
#include <map>
#include "config.hpp"
-#include "database.hpp"
-#include "download.hpp"
+#include "path.hpp"
#include <reaper_plugin.h>
typedef std::function<void()> ActionCallback;
+class Transaction;
+
class ReaPack {
public:
gaccel_register_t action;
+ ReaPack();
+
void init(REAPER_PLUGIN_HINSTANCE, reaper_plugin_info_t *);
void cleanup();
@@ -23,22 +26,18 @@ public:
gaccel_register_t *action, ActionCallback callback);
bool execActions(const int id, const int);
- void synchronizeAll();
- void synchronize(const Repository &);
- void synchronize(Database *);
- void installPackage(Package *);
+ void synchronize();
private:
std::map<int, ActionCallback> m_actions;
Config m_config;
- DownloadQueue m_downloadQueue;
+ Transaction *m_transaction;
REAPER_PLUGIN_HINSTANCE m_instance;
reaper_plugin_info_t *m_rec;
HWND m_mainHandle;
Path m_resourcePath;
- Path m_dbPath;
};
#endif
diff --git a/src/remote.hpp b/src/remote.hpp
@@ -0,0 +1,24 @@
+#ifndef REAPACK_REMOTE_HPP
+#define REAPACK_REMOTE_HPP
+
+#include <string>
+#include <vector>
+
+class Remote;
+typedef std::vector<Remote> RemoteList;
+
+class Remote {
+public:
+ Remote(const std::string &name, const std::string &url)
+ : m_name(name), m_url(url)
+ {}
+
+ const std::string &name() const { return m_name; }
+ const std::string &url() const { return m_url; }
+
+private:
+ std::string m_name;
+ std::string m_url;
+};
+
+#endif
diff --git a/src/transaction.cpp b/src/transaction.cpp
@@ -0,0 +1,134 @@
+#include "transaction.hpp"
+
+#include "errors.hpp"
+
+#include <fstream>
+
+#include <reaper_plugin_functions.h>
+
+using namespace std;
+
+Transaction::Transaction(const Path &root)
+ : m_root(root)
+{
+ m_dbPath = m_root + "ReaPack";
+ RecursiveCreateDirectory(m_dbPath.join().c_str(), 0);
+}
+
+Transaction::~Transaction()
+{
+ for(Database *db : m_databases)
+ delete db;
+}
+
+void Transaction::fetch(const RemoteList &remotes)
+{
+ for(const Remote &remote : remotes)
+ fetch(remote);
+}
+
+void Transaction::fetch(const Remote &remote)
+{
+ Download *dl = new Download(remote.name(), remote.url());
+ dl->addCallback([=]() {
+ if(dl->status() != 200) {
+ addError(dl->contents(), remote.name());
+ return;
+ }
+
+ const Path path = m_dbPath + (remote.name() + ".xml");
+ ofstream file(path.join());
+
+ if(file.bad()) {
+ addError(strerror(errno), remote.name());
+ return;
+ }
+
+ file << dl->contents();
+ file.close();
+
+ Database *db = Database::load(path.join().c_str());
+ db->setName(remote.name());
+
+ m_databases.push_back(db);
+ });
+
+ m_queue.push(dl);
+
+ // execute prepare after the download is deleted, in case finish is called
+ // the queue will also not contain the download anymore
+ dl->addCallback(bind(&Transaction::prepare, this));
+}
+
+void Transaction::prepare()
+{
+ if(!m_queue.empty())
+ return;
+
+ // TODO: make a list of uninstalled or out of date packages only
+ m_packages = m_databases[0]->packages();
+
+ if(m_packages.empty()) {
+ ShowMessageBox("Nothing to do!", "ReaPack", 0);
+ finish();
+ return;
+ }
+
+ m_onReady();
+}
+
+void Transaction::run()
+{
+ for(Package *pkg : m_packages) {
+ try {
+ install(pkg);
+ }
+ catch(const reapack_error &e) {
+ addError(e.what(), pkg->name());
+ }
+ }
+}
+
+void Transaction::install(Package *pkg)
+{
+ const string &url = pkg->lastVersion()->source(0)->url();
+ const Path path = m_root + pkg->targetLocation();
+
+ Download *dl = new Download(pkg->name(), url);
+ dl->addCallback([=] {
+ if(dl->status() != 200) {
+ addError(dl->contents(), dl->name());
+ return;
+ }
+
+ RecursiveCreateDirectory(path.dirname().c_str(), 0);
+
+ ofstream file(path.join());
+ if(file.bad()) {
+ addError(strerror(errno), dl->name());
+ return;
+ }
+
+ file << dl->contents();
+ file.close();
+ });
+
+ m_queue.push(dl);
+
+ // execute finish after the download is deleted
+ // this prevents the download queue from being deleted before the download
+ dl->addCallback(bind(&Transaction::finish, this));
+}
+
+void Transaction::finish()
+{
+ if(!m_queue.empty())
+ return;
+
+ m_onFinish();
+}
+
+void Transaction::addError(const string &message, const string &title)
+{
+ ShowMessageBox(message.c_str(), title.c_str(), 0);
+}
diff --git a/src/transaction.hpp b/src/transaction.hpp
@@ -0,0 +1,47 @@
+#ifndef REAPACK_TRANSACTION_HPP
+#define REAPACK_TRANSACTION_HPP
+
+#include "database.hpp"
+#include "download.hpp"
+#include "path.hpp"
+#include "remote.hpp"
+
+#include <boost/signals2.hpp>
+
+class Remote;
+
+class Transaction {
+public:
+ typedef boost::signals2::signal<void ()> Signal;
+ typedef Signal::slot_type Callback;
+
+ Transaction(const Path &root);
+ ~Transaction();
+
+ void onReady(const Callback &callback) { m_onReady.connect(callback); }
+ void onFinish(const Callback &callback) { m_onFinish.connect(callback); }
+
+ void fetch(const RemoteList &);
+ void fetch(const Remote &);
+
+ void prepare();
+ void run();
+ void finish();
+
+private:
+ void install(Package *);
+ void addError(const std::string &, const std::string &);
+
+ DatabaseList m_databases;
+ PackageList m_packages;
+
+ DownloadQueue m_queue;
+
+ Path m_root;
+ Path m_dbPath;
+
+ Signal m_onReady;
+ Signal m_onFinish;
+};
+
+#endif
diff --git a/src/version.hpp b/src/version.hpp
@@ -6,6 +6,7 @@
#include <vector>
class Source;
+typedef std::vector<Source *> SourceList;
class Version {
public:
@@ -19,7 +20,7 @@ public:
const std::string &changelog() const { return m_changelog; }
void addSource(Source *source);
- const std::vector<Source *> &sources() const { return m_sources; }
+ const SourceList &sources() const { return m_sources; }
Source *source(const int i) const { return m_sources[i]; }
bool operator<(const Version &) const;
@@ -29,7 +30,7 @@ private:
unsigned long m_code;
std::string m_changelog;
- std::vector<Source *> m_sources;
+ SourceList m_sources;
};
class VersionCompare {