reapack

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

commit 7ace89cd3b7bdf8fad5206e5f72b4a2c1b881982
parent bcd6d772988277f6e02d6f8a8e62f6945738717e
Author: cfillion <cfillion@users.noreply.github.com>
Date:   Mon, 25 Jan 2016 22:40:38 -0500

register and unregister scripts in the action list

Diffstat:
Msrc/database.cpp | 4++--
Msrc/database.hpp | 2+-
Msrc/registry.cpp | 150+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
Msrc/registry.hpp | 25+++++++++++++++++++------
Msrc/report.cpp | 8++++----
Msrc/transaction.cpp | 78+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
Msrc/transaction.hpp | 21+++++++++++++--------
Mtest/registry.cpp | 73+++++++++++++++++++++++++++++++++++++++++++++++--------------------------
8 files changed, 265 insertions(+), 96 deletions(-)

diff --git a/src/database.cpp b/src/database.cpp @@ -67,9 +67,9 @@ reapack_error Database::lastError() const return reapack_error(sqlite3_errmsg(m_db)); } -uint64_t Database::lastInsertId() const +int Database::lastInsertId() const { - return (uint64_t)sqlite3_last_insert_rowid(m_db); + return (int)sqlite3_last_insert_rowid(m_db); } int Database::version() const diff --git a/src/database.hpp b/src/database.hpp @@ -37,7 +37,7 @@ public: Statement *prepare(const char *sql); void exec(const char *sql); - uint64_t lastInsertId() const; + int lastInsertId() const; int version() const; int errorCode() const; void begin(); diff --git a/src/registry.cpp b/src/registry.cpp @@ -23,7 +23,6 @@ #include "path.hpp" #include "remote.hpp" -#include <reaper_plugin_functions.h> #include <sqlite3.h> using namespace std; @@ -35,21 +34,37 @@ Registry::Registry(const Path &path) // entry queries m_insertEntry = m_db.prepare( - "INSERT OR REPLACE INTO entries " - "VALUES(NULL, ?, ?, ?, ?);" + "INSERT INTO entries VALUES(NULL, ?, ?, ?, ?, ?, NULL);" + ); + + m_updateEntry = m_db.prepare( + "UPDATE entries SET type = ?, version = ? WHERE id = ?" ); m_findEntry = m_db.prepare( - "SELECT id, version FROM entries " + "SELECT id, remote, category, package, type, version FROM entries " "WHERE remote = ? AND category = ? AND package = ? " "LIMIT 1" ); - m_allEntries = m_db.prepare("SELECT id, version FROM entries WHERE remote = ?"); + m_allEntries = m_db.prepare( + "SELECT id, remote, category, package, type " + "FROM entries WHERE remote = ?" + ); m_forgetEntry = m_db.prepare("DELETE FROM entries WHERE id = ?"); + m_setMainFile = m_db.prepare( + "UPDATE entries SET mainFile = (" + " SELECT id FROM files WHERE path = ?" + ") WHERE id = ?" + ); // file queries m_getFiles = m_db.prepare("SELECT path FROM files WHERE entry = ?"); + m_getMainFile = m_db.prepare( + "SELECT path FROM files WHERE id = (" + " SELECT mainFile FROM entries WHERE id = ? LIMIT 1" + ") LIMIT 1" + ); m_insertFile = m_db.prepare("INSERT INTO files VALUES(NULL, ?, ?)"); m_clearFiles = m_db.prepare( "DELETE FROM files WHERE entry = (" @@ -81,7 +96,9 @@ void Registry::migrate() " remote TEXT NOT NULL," " category TEXT NOT NULL," " package TEXT NOT NULL," + " type INTEGER NOT NULL," " version INTEGER NOT NULL," + " mainFile INTEGER," " UNIQUE(remote, category, package)" ");" @@ -103,7 +120,7 @@ void Registry::migrate() m_db.commit(); } -void Registry::push(Version *ver, vector<Path> *conflicts) +auto Registry::push(Version *ver, vector<Path> *conflicts) -> Entry { m_savepoint->exec(); bool hasConflicts = false; @@ -117,17 +134,29 @@ void Registry::push(Version *ver, vector<Path> *conflicts) m_clearFiles->bind(3, pkg->name()); m_clearFiles->exec(); - m_insertEntry->bind(1, ri->name()); - m_insertEntry->bind(2, cat->name()); - m_insertEntry->bind(3, pkg->name()); - m_insertEntry->bind(4, ver->code()); - m_insertEntry->exec(); + int entryId = getEntry(ver->package()).id; - const uint64_t entryId = m_db.lastInsertId(); + if(entryId) { + m_updateEntry->bind(1, pkg->type()); + m_updateEntry->bind(2, ver->code()); + m_updateEntry->bind(3, entryId); + m_updateEntry->exec(); + } + else { + m_insertEntry->bind(1, ri->name()); + m_insertEntry->bind(2, cat->name()); + m_insertEntry->bind(3, pkg->name()); + m_insertEntry->bind(4, pkg->type()); + m_insertEntry->bind(5, ver->code()); + m_insertEntry->exec(); + + entryId = m_db.lastInsertId(); + } for(const Path &path : ver->files()) { m_insertFile->bind(1, entryId); m_insertFile->bind(2, path.join('/')); + try { m_insertFile->exec(); } @@ -143,15 +172,43 @@ void Registry::push(Version *ver, vector<Path> *conflicts) } } - if(hasConflicts) + if(ver->mainSource()) { + m_setMainFile->bind(1, ver->mainSource()->targetPath().join('/')); + m_setMainFile->bind(2, entryId); + m_setMainFile->exec(); + } + + if(hasConflicts) { m_restore->exec(); - else + return {}; + } + else { m_release->exec(); + return {entryId, ri->name(), cat->name(), + pkg->name(), pkg->type(), ver->code()}; + } } -Registry::Entry Registry::query(Package *pkg) const +auto Registry::query(Package *pkg) const -> QueryResult +{ + const Entry &entry = getEntry(pkg); + + if(!entry.id) + return {}; + + const Status status = + entry.version == pkg->lastVersion()->code() ? UpToDate : UpdateAvailable; + + return {status, entry}; +} + +auto Registry::getEntry(Package *pkg) const -> Entry { int id = 0; + string remote; + string category; + string package; + Package::Type type = Package::UnknownType; uint64_t version = 0; Category *cat = pkg->category(); @@ -162,44 +219,53 @@ Registry::Entry Registry::query(Package *pkg) const m_findEntry->bind(3, pkg->name()); m_findEntry->exec([&] { - id = m_findEntry->intColumn(0); - version = m_findEntry->uint64Column(1); - return false; - }); + int col = 0; - if(!id) - return {id, Uninstalled, 0}; + id = m_findEntry->intColumn(col++); + remote = m_findEntry->stringColumn(col++); + category = m_findEntry->stringColumn(col++); + package = m_findEntry->stringColumn(col++); + type = static_cast<Package::Type>(m_findEntry->intColumn(col++)); + version = m_findEntry->uint64Column(col++); - Version *lastVer = pkg->lastVersion(); + return false; + }); - const Status status = version == lastVer->code() ? UpToDate : UpdateAvailable; - return {id, status, version}; + return {id, remote, category, package, type, version}; } -vector<Registry::Entry> Registry::queryAll(const Remote &remote) const +auto Registry::getEntries(const Remote &remote) const -> vector<Entry> { vector<Registry::Entry> list; m_allEntries->bind(1, remote.name()); m_allEntries->exec([&] { - const int id = m_allEntries->intColumn(0); - const uint64_t version = m_allEntries->uint64Column(1); + int col = 0; + + const int id = m_allEntries->intColumn(col++); + const string &remote = m_allEntries->stringColumn(col++); + const string &category = m_allEntries->stringColumn(col++); + const string &package = m_allEntries->stringColumn(col++); + const Package::Type type = + static_cast<Package::Type>(m_allEntries->intColumn(col++)); + const uint64_t version = m_allEntries->uint64Column(col++); + + list.push_back({id, remote, category, package, type, version}); - list.push_back({id, Unknown, version}); return true; }); return list; } -set<Path> Registry::getFiles(const Entry &qr) const +set<Path> Registry::getFiles(const Entry &entry) const { - if(!qr.id) // skip processing for new packages + if(!entry.id) // skip processing for new packages return {}; set<Path> list; - m_getFiles->bind(1, qr.id); + m_getFiles->bind(1, entry.id); m_getFiles->exec([&] { list.insert(m_getFiles->stringColumn(0)); return true; @@ -208,12 +274,28 @@ set<Path> Registry::getFiles(const Entry &qr) const return list; } -void Registry::forget(const Entry &qr) +string Registry::getMainFile(const Entry &entry) const +{ + if(!entry.id) + return {}; + + string mainFile; + + m_getMainFile->bind(1, entry.id); + m_getMainFile->exec([&] { + mainFile = m_getMainFile->stringColumn(0); + return false; + }); + + return mainFile; +} + +void Registry::forget(const Entry &entry) { - m_forgetFiles->bind(1, qr.id); + m_forgetFiles->bind(1, entry.id); m_forgetFiles->exec(); - m_forgetEntry->bind(1, qr.id); + m_forgetEntry->bind(1, entry.id); m_forgetEntry->exec(); } diff --git a/src/registry.hpp b/src/registry.hpp @@ -23,6 +23,7 @@ #include "path.hpp" #include "database.hpp" +#include "package.hpp" class Package; class Path; @@ -34,22 +35,31 @@ public: Registry(const Path &path = Path()); enum Status { - Unknown, Uninstalled, UpdateAvailable, UpToDate, }; struct Entry { - int id; // internal use - Status status; + int id; + std::string remote; + std::string category; + std::string package; + Package::Type type; uint64_t version; }; - Entry query(Package *) const; - std::vector<Entry> queryAll(const Remote &) const; + struct QueryResult { + Status status; + Entry entry; + }; + + QueryResult query(Package *) const; + Entry getEntry(Package *) const; + std::vector<Entry> getEntries(const Remote &) const; std::set<Path> getFiles(const Entry &) const; - void push(Version *, std::vector<Path> *conflicts = nullptr); + std::string getMainFile(const Entry &) const; + Entry push(Version *, std::vector<Path> *conflicts = nullptr); void forget(const Entry &); void commit(); @@ -58,11 +68,14 @@ private: Database m_db; Statement *m_insertEntry; + Statement *m_updateEntry; Statement *m_findEntry; Statement *m_allEntries; Statement *m_forgetEntry; + Statement *m_setMainFile; Statement *m_getFiles; + Statement *m_getMainFile; Statement *m_insertFile; Statement *m_clearFiles; Statement *m_forgetFiles; diff --git a/src/report.cpp b/src/report.cpp @@ -82,7 +82,7 @@ void Report::printNewPackages() { printHeader("New packages"); - for(const Transaction::PackageEntry &entry : m_transaction->newPackages()) { + for(const Transaction::InstallTicket &entry : m_transaction->newPackages()) { Version *ver = entry.first; m_stream << NL << ver->fullName() << NL; } @@ -92,13 +92,13 @@ void Report::printUpdates() { printHeader("Updates"); - for(const Transaction::PackageEntry &entry : m_transaction->updates()) { + for(const Transaction::InstallTicket &entry : m_transaction->updates()) { Package *pkg = entry.first->package(); - const Registry::Entry &regEntry = entry.second; + const auto &queryRes = entry.second; const VersionSet &versions = pkg->versions(); for(Version *ver : versions | boost::adaptors::reversed) { - if(ver->code() <= regEntry.version) + if(ver->code() <= queryRes.entry.version) break; m_stream << NL << ver->fullName() << NL; diff --git a/src/transaction.cpp b/src/transaction.cpp @@ -90,11 +90,11 @@ void Transaction::upgradeAll(Download *dl) void Transaction::upgrade(Package *pkg) { Version *ver = pkg->lastVersion(); - Registry::Entry entry = m_registry->query(pkg); + auto queryRes = m_registry->query(pkg); try { vector<Path> conflicts; - m_registry->push(ver, &conflicts); + queryRes.entry = m_registry->push(ver, &conflicts); if(!conflicts.empty()) { for(const Path &path : conflicts) { @@ -111,36 +111,38 @@ void Transaction::upgrade(Package *pkg) return; } - if(entry.status == Registry::UpToDate) { + if(queryRes.status == Registry::UpToDate) { if(allFilesExists(ver->files())) return; else - entry.status = Registry::Uninstalled; + queryRes.status = Registry::Uninstalled; } - m_installQueue.push({ver, entry}); + m_installQueue.push({ver, queryRes}); } void Transaction::install() { while(!m_installQueue.empty()) { - const PackageEntry entry = m_installQueue.front(); + const InstallTicket ticket = m_installQueue.front(); m_installQueue.pop(); - Version *ver = entry.first; - const Registry::Entry regEntry = entry.second; - const set<Path> &currentFiles = m_registry->getFiles(regEntry); + Version *ver = ticket.first; + const Registry::QueryResult queryRes = ticket.second; + const set<Path> &currentFiles = m_registry->getFiles(queryRes.entry); InstallTask *task = new InstallTask(ver, currentFiles, this); task->onCommit([=] { - if(regEntry.status == Registry::UpdateAvailable) - m_updates.push_back(entry); + if(queryRes.status == Registry::UpdateAvailable) + m_updates.push_back(ticket); else - m_new.push_back(entry); + m_new.push_back(ticket); const set<Path> &removedFiles = task->removedFiles(); m_removals.insert(removedFiles.begin(), removedFiles.end()); + + registerInHost(true, queryRes.entry); }); addTask(task); @@ -149,17 +151,25 @@ void Transaction::install() runTasks(); } -void Transaction::registerAll(const Remote &) +void Transaction::registerAll(const Remote &remote) { + const vector<Registry::Entry> &entries = m_registry->getEntries(remote); + + for(const auto &entry : entries) + registerInHost(true, entry); } -void Transaction::unregisterAll(const Remote &) +void Transaction::unregisterAll(const Remote &remote) { + const vector<Registry::Entry> &entries = m_registry->getEntries(remote); + + for(const auto &entry : entries) + registerInHost(false, entry); } void Transaction::uninstall(const Remote &remote) { - const vector<Registry::Entry> &entries = m_registry->queryAll(remote); + const vector<Registry::Entry> &entries = m_registry->getEntries(remote); if(entries.empty()) return; @@ -170,6 +180,8 @@ void Transaction::uninstall(const Remote &remote) const set<Path> &files = m_registry->getFiles(entry); allFiles.insert(allFiles.end(), files.begin(), files.end()); + registerInHost(false, entry); + // forget the package even if some files cannot be removed m_registry->forget(entry); } @@ -229,6 +241,8 @@ void Transaction::finish() task->commit(); m_registry->commit(); + + registerScriptsInHost(); } m_onFinish(); @@ -266,3 +280,37 @@ void Transaction::runTasks() if(m_downloadQueue.idle()) finish(); } + +void Transaction::registerInHost(const bool add, const Registry::Entry &entry) +{ + // don't actually do anything until commit() + + switch(entry.type) { + case Package::ScriptType: + m_scriptRegs.push({add, entry, m_registry->getMainFile(entry)}); + break; + default: + break; + } +} + +void Transaction::registerScriptsInHost() +{ + if(!AddRemoveReaScript) { + // do nothing if REAPER < v5.12 + m_scriptRegs = {}; + return; + } + + while(!m_scriptRegs.empty()) { + const HostRegistration &reg = m_scriptRegs.front(); + const std::string &path = Path::prefixRoot(reg.file).join(); + const bool isLast = m_scriptRegs.size() == 1; + + if(!AddRemoveReaScript(reg.add, 0, path.c_str(), isLast) && reg.add) + addError("Script could not be registered in REAPER.", reg.file); + + m_scriptRegs.pop(); + } +} + diff --git a/src/transaction.hpp b/src/transaction.hpp @@ -36,8 +36,8 @@ public: typedef boost::signals2::signal<void ()> Signal; typedef Signal::slot_type Callback; - typedef std::pair<Version *, const Registry::Entry> PackageEntry; - typedef std::vector<PackageEntry> PackageEntryList; + typedef std::pair<Version *, const Registry::QueryResult> InstallTicket; + typedef std::vector<InstallTicket> InstallTicketList; struct Error { std::string message; @@ -63,14 +63,13 @@ public: DownloadQueue *downloadQueue() { return &m_downloadQueue; } size_t taskCount() const { return m_tasks.size(); } - const PackageEntryList &newPackages() const { return m_new; } - const PackageEntryList &updates() const { return m_updates; } + const InstallTicketList &newPackages() const { return m_new; } + const InstallTicketList &updates() const { return m_updates; } const std::set<Path> &removals() const { return m_removals; } const ErrorList &errors() const { return m_errors; } bool saveFile(Download *, const Path &); void addError(const std::string &msg, const std::string &title); - Path prefixPath(const Path &) const; private: void install(); @@ -81,18 +80,24 @@ private: bool allFilesExists(const std::set<Path> &) const; void addTask(Task *); + void registerInHost(bool add, const Registry::Entry &); + void registerScriptsInHost(); + bool m_isCancelled; Registry *m_registry; std::set<Remote> m_remotes; std::vector<RemoteIndex *> m_remoteIndexes; DownloadQueue m_downloadQueue; - std::queue<PackageEntry> m_installQueue; + std::queue<InstallTicket> m_installQueue; std::vector<Task *> m_tasks; std::queue<Task *> m_taskQueue; - PackageEntryList m_new; - PackageEntryList m_updates; + struct HostRegistration { bool add; Registry::Entry entry; std::string file; }; + std::queue<HostRegistration> m_scriptRegs; + + InstallTicketList m_new; + InstallTicketList m_updates; std::set<Path> m_removals; ErrorList m_errors; diff --git a/test/registry.cpp b/test/registry.cpp @@ -15,7 +15,7 @@ static const char *M = "[registry]"; #define MAKE_PACKAGE \ RemoteIndex ri("Remote Name"); \ - Category cat("Hello", &ri); \ + Category cat("Category Name", &ri); \ Package pkg(Package::ScriptType, "Hello", &cat); \ Version *ver = new Version("1.0", &pkg); \ Source *src = new Source(Source::GenericPlatform, "file", "url", ver); \ @@ -27,20 +27,28 @@ TEST_CASE("query uninstalled package", M) { Registry reg; - const Registry::Entry res = reg.query(&pkg); + const auto &res = reg.query(&pkg); REQUIRE(res.status == Registry::Uninstalled); - REQUIRE(res.version == 0); + REQUIRE(res.entry.id == 0); + REQUIRE(res.entry.version == 0); } TEST_CASE("query up to date pacakge", M) { MAKE_PACKAGE Registry reg; - reg.push(ver); - const Registry::Entry res = reg.query(&pkg); - REQUIRE(res.status == Registry::UpToDate); - REQUIRE(res.version == Version("1.0").code()); + const Registry::Entry &entry = reg.push(ver); + REQUIRE(entry.id == 1); + REQUIRE(entry.remote == "Remote Name"); + REQUIRE(entry.category == "Category Name"); + REQUIRE(entry.package == "Hello"); + REQUIRE(entry.type == Package::ScriptType); + REQUIRE(entry.version == Version("1.0").code()); + + const Registry::QueryResult &queryRes = reg.query(&pkg); + REQUIRE(queryRes.status == Registry::UpToDate); + REQUIRE(queryRes.entry.version == Version("1.0").code()); } TEST_CASE("bump version", M) { @@ -53,26 +61,27 @@ TEST_CASE("bump version", M) { reg.push(ver); pkg.addVersion(ver2); - const Registry::Entry res1 = reg.query(&pkg); + const Registry::QueryResult &res1 = reg.query(&pkg); REQUIRE(res1.status == Registry::UpdateAvailable); - REQUIRE(res1.version == Version("1.0").code()); + REQUIRE(res1.entry.version == Version("1.0").code()); reg.push(ver2); - const Registry::Entry res2 = reg.query(&pkg); + const Registry::QueryResult &res2 = reg.query(&pkg); REQUIRE(res2.status == Registry::UpToDate); - REQUIRE(res2.version == Version("2.0").code()); + REQUIRE(res2.entry.version == Version("2.0").code()); + + REQUIRE(res2.entry.id == res1.entry.id); } TEST_CASE("get file list", M) { MAKE_PACKAGE Registry reg; - REQUIRE(reg.getFiles(reg.query(&pkg)).empty()); + REQUIRE(reg.getFiles(reg.query(&pkg).entry).empty()); reg.push(ver); - const Registry::Entry res = reg.query(&pkg); - const set<Path> files = reg.getFiles(res); + const set<Path> &files = reg.getFiles(reg.query(&pkg).entry); REQUIRE(files == ver->files()); } @@ -83,29 +92,28 @@ TEST_CASE("query all packages", M) { const Remote remote("Remote Name", "irrelevent_url"); Registry reg; - REQUIRE(reg.queryAll(remote).empty()); + REQUIRE(reg.getEntries(remote).empty()); reg.push(ver); - const vector<Registry::Entry> entries = reg.queryAll(remote); + const vector<Registry::Entry> &entries = reg.getEntries(remote); REQUIRE(entries.size() == 1); REQUIRE(entries[0].id == 1); - REQUIRE(entries[0].status == Registry::Unknown); - REQUIRE(entries[0].version == ver->code()); + REQUIRE(entries[0].remote == "Remote Name"); + REQUIRE(entries[0].category == "Category Name"); + REQUIRE(entries[0].package == "Hello"); + REQUIRE(entries[0].type == Package::ScriptType); } TEST_CASE("forget registry entry", M) { MAKE_PACKAGE Registry reg; - reg.push(ver); - - reg.forget(reg.query(&pkg)); + reg.forget(reg.push(ver)); - const Registry::Entry afterForget = reg.query(&pkg); - REQUIRE(afterForget.id == 0); + const Registry::QueryResult &afterForget = reg.query(&pkg); REQUIRE(afterForget.status == Registry::Uninstalled); - REQUIRE(afterForget.version == 0); + REQUIRE(afterForget.entry.id == 0); } TEST_CASE("file conflicts", M) { @@ -117,7 +125,7 @@ TEST_CASE("file conflicts", M) { } RemoteIndex ri("Remote Name"); - Category cat("Hello", &ri); + Category cat("Category Name", &ri); Package pkg(Package::ScriptType, "Duplicate Package", &cat); Version *ver = new Version("1.0", &pkg); Source *src1 = new Source(Source::GenericPlatform, "file", "url", ver); @@ -130,7 +138,7 @@ TEST_CASE("file conflicts", M) { try { reg.push(ver); - FAIL("duplicate was accepted"); + FAIL("duplicate is accepted"); } catch(const reapack_error &) {} @@ -144,3 +152,16 @@ TEST_CASE("file conflicts", M) { REQUIRE(reg.query(&pkg).status == Registry::Uninstalled); } + +TEST_CASE("get main file", M) { + MAKE_PACKAGE + + Registry reg; + REQUIRE(reg.getMainFile({}).empty()); + + Source *main = new Source(Source::GenericPlatform, {}, "url", ver); + ver->addSource(main); + + const Registry::Entry &entry = reg.push(ver); + REQUIRE(reg.getMainFile(entry) == main->targetPath().join('/')); +}