reapack

Package manager for REAPER
Log | Files | Refs | Submodules | README | LICENSE

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:
Msrc/config.cpp | 38+++++++++++++++++---------------------
Msrc/config.hpp | 27+++++----------------------
Msrc/database.hpp | 16++++++++++------
Msrc/download.cpp | 24++++++++++--------------
Msrc/download.hpp | 16+++++++++-------
Msrc/main.cpp | 2+-
Msrc/package.hpp | 5++++-
Msrc/reapack.cpp | 107++++++++++++++++---------------------------------------------------------------
Msrc/reapack.hpp | 15+++++++--------
Asrc/remote.hpp | 24++++++++++++++++++++++++
Asrc/transaction.cpp | 134+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/transaction.hpp | 47+++++++++++++++++++++++++++++++++++++++++++++++
Msrc/version.hpp | 5+++--
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 {