reapack

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

commit 14ccc6444ec29e73469e98b7477e2a30198b9751
parent a2d8dd75cdfd063a9bbc995445d1bc804b760eb2
Author: cfillion <cfillion@users.noreply.github.com>
Date:   Sat, 12 Dec 2015 15:22:27 -0500

prepare the code for the installation of multifile packages

Diffstat:
Msrc/source.cpp | 23++++++++++++++++++++++-
Msrc/source.hpp | 13+++++++++++++
Msrc/transaction.cpp | 184+++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
Msrc/transaction.hpp | 37++++++++++++++++++++++++++++++++++---
Msrc/version.cpp | 2++
Mtest/version.cpp | 14+++++++++++++-
6 files changed, 202 insertions(+), 71 deletions(-)

diff --git a/src/source.cpp b/src/source.cpp @@ -1,6 +1,10 @@ #include "source.hpp" #include "errors.hpp" +#include "package.hpp" +#include "version.hpp" + +using namespace std; Source::Platform Source::ConvertPlatform(const char *platform) { @@ -23,8 +27,25 @@ Source::Platform Source::ConvertPlatform(const char *platform) } Source::Source(const Platform platform, const std::string &url) - : m_platform(platform), m_url(url) + : m_platform(platform), m_url(url), m_version(nullptr) { if(m_url.empty()) throw reapack_error("empty source url"); } + +Package *Source::package() const +{ + return m_version ? m_version->package() : nullptr; +} + +string Source::fullName() const +{ + // TODO + return m_version->fullName(); +} + +Path Source::targetPath() const +{ + // TODO + return package()->targetPath(); +} diff --git a/src/source.hpp b/src/source.hpp @@ -4,9 +4,14 @@ #include <string> #include <vector> +#include "path.hpp" + class Source; typedef std::vector<Source *> SourceList; +class Package; +class Version; + class Source { public: enum Platform { @@ -31,9 +36,17 @@ public: Platform platform() const { return m_platform; } const std::string &url() const { return m_url; } + void setVersion(Version *ver) { m_version = ver; } + Version *version() const { return m_version; } + Package *package() const; + + std::string fullName() const; + Path targetPath() const; + private: Platform m_platform; std::string m_url; + Version *m_version; }; #endif diff --git a/src/transaction.cpp b/src/transaction.cpp @@ -17,6 +17,9 @@ Transaction::Transaction(Registry *reg, const Path &root) Transaction::~Transaction() { + for(PackageTransaction *tr : m_transactions) + delete tr; + for(Database *db : m_databases) delete db; } @@ -30,33 +33,7 @@ void Transaction::fetch(const RemoteMap &remotes) void Transaction::fetch(const Remote &remote) { Download *dl = new Download(remote.first, remote.second); - dl->onFinish([=]() { - if(dl->status() != 200) { - addError(dl->contents(), dl->url()); - return; - } - - const Path path = m_dbPath + ("remote_" + dl->name() + ".xml"); - ofstream file(path.join()); - - if(file.bad()) { - addError(strerror(errno), path.join()); - return; - } - - file << dl->contents(); - file.close(); - - try { - Database *db = Database::load(path.join().c_str()); - db->setName(dl->name()); - - m_databases.push_back(db); - } - catch(const reapack_error &e) { - addError(e.what(), dl->url()); - } - }); + dl->onFinish(bind(&Transaction::saveDatabase, this, dl)); m_queue.push(dl); @@ -65,6 +42,24 @@ void Transaction::fetch(const Remote &remote) dl->onFinish(bind(&Transaction::prepare, this)); } +void Transaction::saveDatabase(Download *dl) +{ + const Path path = m_dbPath + ("remote_" + dl->name() + ".xml"); + + if(!saveFile(dl, path)) + return; + + try { + Database *db = Database::load(path.join().c_str()); + db->setName(dl->name()); + + m_databases.push_back(db); + } + catch(const reapack_error &e) { + addError(e.what(), dl->url()); + } +} + void Transaction::prepare() { if(!m_queue.idle()) @@ -73,7 +68,7 @@ void Transaction::prepare() for(Database *db : m_databases) { for(Package *pkg : db->packages()) { Registry::QueryResult entry = m_registry->query(pkg); - bool exists = file_exists(installPath(pkg).join().c_str()); + bool exists = PackageTransaction::isInstalled(pkg->lastVersion(), m_root); if(entry.status == Registry::UpToDate && exists) continue; @@ -90,12 +85,30 @@ void Transaction::prepare() void Transaction::run() { - for(const PackageEntry &pkg : m_packages) { + for(const PackageEntry &entry : m_packages) { + Package *pkg = entry.first; + Registry::QueryResult regEntry = entry.second; + + PackageTransaction *tr = new PackageTransaction(this); + try { - install(pkg); + tr->install(entry.first->lastVersion()); + tr->onFinish([=] { + if(regEntry.status == Registry::UpdateAvailable) + m_updates.push_back(entry); + else + m_new.push_back(entry); + + m_registry->push(pkg); + + finish(); + }); + + m_transactions.push_back(tr); } catch(const reapack_error &e) { - addError(e.what(), pkg.first->fullName()); + addError(e.what(), pkg->fullName()); + delete tr; } } } @@ -106,61 +119,100 @@ void Transaction::cancel() m_queue.abort(); } -void Transaction::install(const PackageEntry &pkgEntry) +bool Transaction::saveFile(Download *dl, const Path &path) { - Package *pkg = pkgEntry.first; - Version *ver = pkg->lastVersion(); + if(dl->status() != 200) { + addError(dl->contents(), dl->url()); + return false; + } - const Path path = installPath(pkg); - const Registry::QueryResult regEntry = pkgEntry.second; + RecursiveCreateDirectory(path.dirname().c_str(), 0); - Download *dl = new Download(ver->fullName(), ver->source(0)->url()); - dl->onFinish([=] { - if(dl->status() != 200) { - addError(dl->contents(), dl->url()); - return; - } + const string strPath = path.join(); + ofstream file(strPath); - RecursiveCreateDirectory(path.dirname().c_str(), 0); + if(file.bad()) { + addError(strerror(errno), strPath); + return false; + } - ofstream file(path.join()); - if(file.bad()) { - addError(strerror(errno), path.join()); - return; - } + file << dl->contents(); + file.close(); - file << dl->contents(); - file.close(); + return true; +} - if(regEntry.status == Registry::UpdateAvailable) - m_updates.push_back(pkgEntry); - else - m_new.push_back(pkgEntry); +void Transaction::finish() +{ + if(!m_queue.idle()) + return; - m_registry->push(pkg); - }); + m_onFinish(); +} - m_queue.push(dl); +void Transaction::addError(const string &message, const string &title) +{ + m_errors.push_back({message, title}); +} - // execute finish after the download is deleted - // this prevents the download queue from being deleted before the download is - dl->onFinish(bind(&Transaction::finish, this)); +bool PackageTransaction::isInstalled(Version *ver, const Path &root) +{ + // TODO + // file_exists(installPath(pkg).join().c_str()); + return true; } -Path Transaction::installPath(Package *pkg) const +PackageTransaction::PackageTransaction(Transaction *transaction) + : m_transaction(transaction), m_remaining(0) { - return m_root + pkg->targetPath(); } -void Transaction::finish() +void PackageTransaction::install(Version *ver) { - if(!m_queue.idle()) + const size_t sourcesSize = ver->sources().size(); + + for(size_t i = 0; i < sourcesSize; i++) { + Source *src = ver->source(i); + + Download *dl = new Download(src->fullName(), src->url()); + dl->onFinish(bind(&PackageTransaction::saveSource, this, dl, src)); + + m_remaining.push_back(dl); + m_transaction->downloadQueue()->push(dl); + + // executing finish after the download is deleted + // prevents the download queue from being deleted before the download is + dl->onFinish(bind(&PackageTransaction::finish, this)); + } +} + +void PackageTransaction::saveSource(Download *dl, Source *src) +{ + m_remaining.erase(remove(m_remaining.begin(), m_remaining.end(), dl)); + + const Path path = installPath(src); + + if(!m_transaction->saveFile(dl, path)) { + abort(); + return; + } +} + +void PackageTransaction::finish() +{ + if(!m_remaining.empty()) return; m_onFinish(); } -void Transaction::addError(const string &message, const string &title) +void PackageTransaction::abort() { - m_errors.push_back({message, title}); + for(Download *dl : m_remaining) + dl->abort(); +} + +Path PackageTransaction::installPath(Source *src) const +{ + return m_transaction->m_root + src->targetPath(); } diff --git a/src/transaction.hpp b/src/transaction.hpp @@ -9,6 +9,8 @@ #include <boost/signals2.hpp> +class PackageTransaction; + class Transaction { public: typedef boost::signals2::signal<void ()> Signal; @@ -24,7 +26,6 @@ public: typedef std::vector<const Error> ErrorList; - Transaction(Registry *reg, const Path &root); ~Transaction(); @@ -44,12 +45,14 @@ public: const ErrorList &errors() const { return m_errors; } private: + friend PackageTransaction; + void prepare(); void finish(); - void install(const PackageEntry &); + void saveDatabase(Download *); + bool saveFile(Download *, const Path &); void addError(const std::string &msg, const std::string &title); - Path installPath(Package *) const; Registry *m_registry; @@ -64,8 +67,36 @@ private: PackageEntryList m_updates; ErrorList m_errors; + std::vector<PackageTransaction *> m_transactions; + Signal m_onReady; Signal m_onFinish; }; +class PackageTransaction { +public: + static bool isInstalled(Version *, const Path &root); + + typedef boost::signals2::signal<void ()> Signal; + typedef Signal::slot_type Callback; + + PackageTransaction(Transaction *parent); + + void onFinish(const Callback &callback) { m_onFinish.connect(callback); } + + void install(Version *ver); + void abort(); + +private: + void saveSource(Download *, Source *); + Path installPath(Source *) const; + + void finish(); + + Transaction *m_transaction; + std::vector<Download *> m_remaining; + + Signal m_onFinish; +}; + #endif diff --git a/src/version.cpp b/src/version.cpp @@ -75,6 +75,8 @@ void Version::addSource(Source *source) return; #endif #endif + + source->setVersion(this); m_sources.push_back(source); } diff --git a/test/version.cpp b/test/version.cpp @@ -95,7 +95,19 @@ TEST_CASE("version full name", M) { REQUIRE(ver.fullName() == "Database Name/Category Name/file.name v1.0"); } -TEST_CASE("drop sources for unknown platforms") { +TEST_CASE("add source", M) { + Source *src = new Source(Source::GenericPlatform, "a"); + + Version ver("1"); + CHECK(ver.sources().size() == 0); + ver.addSource(src); + CHECK(ver.sources().size() == 1); + + REQUIRE(src->version() == &ver); + REQUIRE(ver.source(0) == src); +} + +TEST_CASE("drop sources for unknown platforms", M) { Version ver("1"); ver.addSource(new Source(Source::UnknownPlatform, "a"));