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:
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"));