From aacff79e854d6d4eb22540339bc88c3efab353a2 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Thu, 5 Nov 2015 17:41:16 +0200 Subject: Implement package dependency resolution --- brep/package | 124 +++++++++++++++++++++++++++------------ brep/package-details.cxx | 24 ++++---- brep/package-search.cxx | 17 +++--- brep/package-version-details.cxx | 35 +++++++++-- brep/package.cxx | 10 +++- brep/page.cxx | 27 ++++++--- 6 files changed, 168 insertions(+), 69 deletions(-) (limited to 'brep') diff --git a/brep/package b/brep/package index 719a906..ef73542 100644 --- a/brep/package +++ b/brep/package @@ -10,6 +10,7 @@ #include #include #include // shared_ptr +#include #include // size_t #include // move() #include // uint16 @@ -75,13 +76,11 @@ namespace brep class package; using strings = std::vector; - - template - using optional = butl::optional; + using butl::optional; // path // - using path = butl::path; + using butl::path; #pragma db map type(path) as(std::string) \ to((?).string ()) from(brep::path (?)) @@ -93,14 +92,14 @@ namespace brep to((?) ? (?)->string () : brep::optional_string ()) \ from((?) ? brep::path (*(?)) : brep::optional_path ()) - using dir_path = butl::dir_path; + using butl::dir_path; #pragma db map type(dir_path) as(std::string) \ to((?).string ()) from(brep::dir_path (?)) // timestamp // - using timestamp = butl::timestamp; + using butl::timestamp; #pragma db map type(timestamp) as(std::uint64_t) \ to(std::chrono::system_clock::to_time_t (?)) \ @@ -108,7 +107,7 @@ namespace brep // version // - using version = bpkg::version; + using bpkg::version; #pragma db value struct canonical_version @@ -116,6 +115,18 @@ namespace brep std::uint16_t epoch; std::string canonical_upstream; std::uint16_t revision; + + bool + empty () const noexcept + { + // No sense to test epoch and revision for 0 as a valid canonical_version + // object can not have them different from 0 if canonical_upstream is + // empty. The predicate semantics is equal to the one of the + // bpkg::version class. + // + assert (!canonical_upstream.empty () || (epoch == 0 && revision == 0)); + return canonical_upstream.empty (); + } }; #pragma db value transient @@ -140,14 +151,14 @@ namespace brep // priority // - using priority = bpkg::priority; + using bpkg::priority; #pragma db value(priority) definition #pragma db member(priority::value) column("") // url // - using url = bpkg::url; + using bpkg::url; #pragma db value(url) definition #pragma db member(url::value) virtual(std::string) before access(this) \ @@ -155,7 +166,7 @@ namespace brep // email // - using email = bpkg::email; + using bpkg::email; #pragma db value(email) definition #pragma db member(email::value) virtual(std::string) before access(this) \ @@ -163,17 +174,33 @@ namespace brep // licenses // - using licenses = bpkg::licenses; + using bpkg::licenses; using license_alternatives = std::vector; #pragma db value(licenses) definition // dependencies // - using comparison = bpkg::comparison; - using dependency_constraint = bpkg::dependency_constraint; + using bpkg::comparison; + using bpkg::dependency_constraint; #pragma db value(dependency_constraint) definition + #pragma db member(dependency_constraint::operation) column("") + #pragma db member(dependency_constraint::version) column("") + + #pragma db value + struct package_id + { + std::string name; + canonical_version version; + + package_id () = default; + package_id (std::string n, const brep::version& v) + : name (std::move (n)), + version {v.epoch, v.canonical_upstream, v.revision} + { + } + }; // Notes: // @@ -215,24 +242,51 @@ namespace brep // * No need to complicate persisted object model with repository // relations otherwise required just for dependency resolution. // - using dependency = bpkg::dependency; - using dependency_alternatives = bpkg::dependency_alternatives; - using dependencies = std::vector; - #pragma db value(dependency) definition - #pragma db member(dependency::constraint) column("") - #pragma db value(dependency_alternatives) definition + #pragma db value + struct dependency + { + using package_type = brep::package; + + odb::lazy_shared_ptr package; + optional constraint; + + // Prerequisite package name. + // + std::string + name () const; + + // Database mapping. + // + #pragma db member(package) column("") not_null + }; + + #pragma db value + class dependency_alternatives: public std::vector + { + public: + bool conditional; + std::string comment; + + dependency_alternatives () = default; + + explicit + dependency_alternatives (bool d, std::string c) + : conditional (d), comment (std::move (c)) {} + }; + + using dependencies = std::vector; // requirements // - using requirement_alternatives = bpkg::requirement_alternatives; + using bpkg::requirement_alternatives; using requirements = std::vector; #pragma db value(requirement_alternatives) definition // repository_location // - using repository_location = bpkg::repository_location; + using bpkg::repository_location; #pragma db map type(repository_location) as(std::string) \ to((?).string ()) from(brep::repository_location (?)) @@ -270,6 +324,8 @@ namespace brep timestamp repositories_timestamp; bool internal; + std::vector> complements; + std::vector> prerequisites; // Database mapping. // @@ -279,25 +335,17 @@ namespace brep set(this.location = std::move (?); \ assert (this.name == this.location.canonical_name ())) + #pragma db member(complements) id_column("repository") \ + value_column("complement") value_not_null + + #pragma db member(prerequisites) id_column("repository") \ + value_column("prerequisite") value_not_null + private: friend class odb::access; repository () = default; }; - #pragma db value - struct package_id - { - std::string name; - canonical_version version; - - package_id () = default; - package_id (std::string n, const brep::version& v) - : name (std::move (n)), - version {v.epoch, v.canonical_upstream, v.revision} - { - } - }; - // The 'to' expression calls the PostgreSQL to_tsvector(weighted_text) // function overload (package-extra.sql). Since we are only interested // in "write-only" members of this type, make the 'from' expression @@ -382,7 +430,7 @@ namespace brep // optional location; - std::vector> external_repositories; + std::vector> other_repositories; // Database mapping. // @@ -446,9 +494,9 @@ namespace brep set(odb::nested_set (this.requirements, move (?))) \ id_column("") key_column("") value_column("id") - // external_repositories + // other_repositories // - #pragma db member(external_repositories) \ + #pragma db member(other_repositories) \ id_column("") value_column("repository") value_not_null // search_index diff --git a/brep/package-details.cxx b/brep/package-details.cxx index f814ef8..0424d5b 100644 --- a/brep/package-details.cxx +++ b/brep/package-details.cxx @@ -10,6 +10,7 @@ #include +#include #include #include @@ -119,6 +120,7 @@ namespace brep << A(HREF=url (!f, sq, pg)) << (f ? "[brief]" : "[full]") << ~A << ~DIV; + session sn; transaction t (db_->begin ()); shared_ptr p; @@ -172,13 +174,12 @@ namespace brep "OFFSET" + to_string (pg * rp) + "LIMIT" + to_string (rp))); - s << FORM_SEARCH (sq.c_str ()) - << DIV_COUNTER (pc, "Version", "Versions") - - // Enclose the subsequent tables to be able to use nth-child CSS selector. - // - << DIV; + s << FORM_SEARCH (sq) + << DIV_COUNTER (pc, "Version", "Versions"); + // Enclose the subsequent tables to be able to use nth-child CSS selector. + // + s << DIV; for (const auto& pr: r) { shared_ptr p (db_->load (pr.id)); @@ -187,7 +188,7 @@ namespace brep << TBODY << TR_VERSION (name, p->version.string ()) - // @@ Shouldn't we skip low priority row ? + // @@ Shouldn't we skip low priority row ? Don't think so, why? // << TR_PRIORITY (p->priority); @@ -200,24 +201,27 @@ namespace brep assert (p->internal_repository != nullptr); // @@ Shouldn't we make package location to be a link to the proper - // place of the About page, describing corresponding repository ? + // place of the About page, describing corresponding repository? + // Yes, I think that's sounds reasonable, once we have about. // // @@ In most cases package location will be the same for all versions // of the same package. Shouldn't we put package location to the // package summary part and display it here only if it differes // from the one in the summary ? // + // Hm, I am not so sure about this. Consider: stable/testing/unstable. + // s << TR_LOCATION (p->internal_repository.object_id ()) << TR_DEPENDS (p->dependencies) << TR_REQUIRES (p->requirements) << ~TBODY << ~TABLE; } + s << ~DIV; t.commit (); - s << ~DIV - << DIV_PAGER (pg, pc, rp, options_->pages_in_pager (), url (f, sq)) + s << DIV_PAGER (pg, pc, rp, options_->pages_in_pager (), url (f, sq)) << ~DIV << ~BODY << ~HTML; diff --git a/brep/package-search.cxx b/brep/package-search.cxx index 64e43f1..47f53e9 100644 --- a/brep/package-search.cxx +++ b/brep/package-search.cxx @@ -10,6 +10,7 @@ #include +#include #include #include @@ -92,6 +93,7 @@ namespace brep << DIV_HEADER () << DIV(ID="content"); + session sn; transaction t (db_->begin ()); auto pc ( @@ -105,13 +107,12 @@ namespace brep "OFFSET" + to_string (pg * rp) + "LIMIT" + to_string (rp))); - s << FORM_SEARCH (sq.c_str ()) - << DIV_COUNTER (pc, "Package", "Packages") - - // Enclose the subsequent tables to be able to use nth-child CSS selector. - // - << DIV; + s << FORM_SEARCH (sq) + << DIV_COUNTER (pc, "Package", "Packages"); + // Enclose the subsequent tables to be able to use nth-child CSS selector. + // + s << DIV; for (const auto& pr: r) { shared_ptr p (db_->load (pr.id)); @@ -127,13 +128,13 @@ namespace brep << ~TBODY << ~TABLE; } + s << ~DIV; t.commit (); string url (qp.empty () ? "/" : ("/?" + qp)); - s << ~DIV - << DIV_PAGER (pg, pc, rp, options_->pages_in_pager (), url) + s << DIV_PAGER (pg, pc, rp, options_->pages_in_pager (), url) << ~DIV << ~BODY << ~HTML; diff --git a/brep/package-version-details.cxx b/brep/package-version-details.cxx index 71559b9..a4fa95a 100644 --- a/brep/package-version-details.cxx +++ b/brep/package-version-details.cxx @@ -11,6 +11,7 @@ #include +#include #include #include @@ -63,7 +64,7 @@ namespace brep } assert (i != rq.path ().rend ()); - const string& n (*i); + const string& n (*i); // Package name. params::package_version_details pr; @@ -120,6 +121,7 @@ namespace brep bool not_found (false); shared_ptr p; + session sn; transaction t (db_->begin ()); try @@ -153,8 +155,6 @@ namespace brep const string du (p->internal_repository.load ()->location.string () + "/" + p->location->string ()); - t.commit (); - s << TABLE(CLASS="proplist", ID="version") << TBODY @@ -209,7 +209,32 @@ namespace brep if (&d != &da[0]) s << " | "; - s << d; // @@ Should it be a link ? + shared_ptr p (d.package.load ()); + string en (mime_url_encode (p->id.name)); + + if (p->internal_repository != nullptr) + s << A << HREF << "/go/" << en << ~HREF << p->id.name << ~A; + else + // @@ Refer to package repository URL when supported in repository + // manifest. + // + s << p->id.name; + + if (d.constraint) + { + s << ' '; + + if (p->internal_repository != nullptr) + s << A + << HREF << "/go/" << en << "/" << p->version.string () << ~HREF + << *d.constraint + << ~A; + else + // @@ Refer to package repository URL when supported in + // repository manifest. + // + s << *d.constraint; + } } s << ~SPAN @@ -222,6 +247,8 @@ namespace brep << ~TABLE; } + t.commit (); + const auto& rt (p->requirements); if (!rt.empty ()) diff --git a/brep/package.cxx b/brep/package.cxx index 9b7d24a..3c65ed8 100644 --- a/brep/package.cxx +++ b/brep/package.cxx @@ -16,6 +16,14 @@ using namespace odb::core; namespace brep { + // dependency + // + string dependency:: + name () const + { + return package.object_id ().name; + } + // package // package:: @@ -63,7 +71,7 @@ namespace brep version (move (vr)) { assert (!rp->internal); - external_repositories.emplace_back (move (rp)); + other_repositories.emplace_back (move (rp)); } weighted_text package:: diff --git a/brep/page.cxx b/brep/page.cxx index a23ce17..deee055 100644 --- a/brep/page.cxx +++ b/brep/page.cxx @@ -6,6 +6,7 @@ #include #include +#include // shared_ptr #include #include // move() #include // min() @@ -16,6 +17,7 @@ #include #include +#include using namespace std; using namespace xml; @@ -51,7 +53,7 @@ namespace brep void FORM_SEARCH:: operator() (serializer& s) const { - // The 'action' attribute is optional in HTML5. While the standard don't + // The 'action' attribute is optional in HTML5. While the standard doesn't // specify browser behavior explicitly for the case the attribute is // ommited, the only reasonable behavior is to default it to the current // document URL. @@ -260,26 +262,35 @@ namespace brep // set ds; for (const auto& da: d) - ds.emplace (da.name); + ds.emplace (da.name ()); bool m (ds.size () > 1); if (m) s << "("; - bool first (true); + bool f (true); // First dependency alternative. for (const auto& da: d) { - if (ds.find (da.name) != ds.end ()) + string n (da.name ()); + if (ds.find (n) != ds.end ()) { - ds.erase (da.name); + ds.erase (n); - if (first) - first = false; + if (f) + f = false; else s << " | "; - s << da.name; // @@ Make it a link. + shared_ptr p (da.package.load ()); + + if (p->internal_repository != nullptr) + s << A << HREF << "/go/" << mime_url_encode (n) << ~HREF << n << ~A; + else + // @@ Refer to package repository URL when supported in repository + // manifest. + // + s << n; } } -- cgit v1.1