From a2b084651909929d58f6b4bc0f3c742d87ee31f6 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Fri, 27 Apr 2018 15:53:00 +0300 Subject: Add support for repository fragments --- bpkg/package.hxx | 287 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 191 insertions(+), 96 deletions(-) (limited to 'bpkg/package.hxx') diff --git a/bpkg/package.hxx b/bpkg/package.hxx index 5b0db8c..ff05301 100644 --- a/bpkg/package.hxx +++ b/bpkg/package.hxx @@ -241,28 +241,62 @@ namespace bpkg : (?).type ()}) \ from(bpkg::repository_location (std::move ((?).url), (?).type)) - // repository + // repository_fragment + // + // Some repository types (normally version control-based) can be + // fragmented. For example, a git repository consists of multiple commits + // (fragments) which could contain different sets of packages and even + // prerequisite/complement repositories. Note also that the same fragment + // could be shared by multiple repository objects. We assume a fragment to + // be immutable, so it's complement, prerequisite and package sets can never + // change. + // + // For repository types that do not support fragmentation, there should + // be a single repository_fragment with the name and location equal to the + // ones of the containing repository. Such a fragment can not be shared but + // can be changed. // + // One of the consequences of the above is that a fragment can either be + // shared or be mutable. + // + class repository; + #pragma db object pointer(shared_ptr) session - class repository + class repository_fragment { public: - // We use a weak pointer for prerequisite repositories because we - // could have cycles. No cycles in complements, thought. + // We use a weak pointer for prerequisite repositories because we could + // have cycles. No cycles in complements, thought. + // + // Note that these point to repositories, not repository fragments. // using complements_type = std::set, compare_lazy_ptr>; using prerequisites_type = std::set, compare_lazy_ptr>; + // Repository fragment id is a repository canonical name that identifies + // just this fragment (for example, for git it is a canonical name of + // the repository URL with the full, non-abbreviated commit id). + // + // Note that while this works naturally for git where the fragment (full + // commit id) is also a valid fragment filter, it may not fit some future + // repository types. Let's deal with it when we see such a beast. + // string name; // Object id (canonical name). + + // For version control-based repositories it is used for a package + // checkout, that may involve communication with the remote repository. + // repository_location location; - complements_type complements; + + complements_type complements; prerequisites_type prerequisites; public: explicit - repository (repository_location l): location (move (l)) + repository_fragment (repository_location l) + : location (move (l)) { name = location.canonical_name (); } @@ -275,14 +309,66 @@ namespace bpkg set(this.location = std::move (?); \ assert (this.name == this.location.canonical_name ())) - #pragma db member(complements) id_column("repository") \ + #pragma db member(complements) id_column("repository_fragment") \ value_column("complement") value_not_null - #pragma db member(prerequisites) id_column("repository") \ + #pragma db member(prerequisites) id_column("repository_fragment") \ value_column("prerequisite") value_not_null private: friend class odb::access; + repository_fragment () = default; + }; + + #pragma db view object(repository_fragment) \ + query(repository_fragment::name != "" && (?)) + struct repository_fragment_count + { + #pragma db column("count(*)") + size_t result; + + operator size_t () const {return result;} + }; + + // repository + // + #pragma db object pointer(shared_ptr) session + class repository + { + public: + #pragma db value + struct fragment_type + { + string friendly_name; // User-friendly fragment name (e.g, tag, etc). + lazy_shared_ptr fragment; + }; + + using fragments_type = std::vector; + + string name; // Object id (canonical name). + repository_location location; + fragments_type fragments; + + public: + explicit + repository (repository_location l): location (move (l)) + { + name = location.canonical_name (); + } + + // Database mapping. + // + #pragma db member(name) id + + #pragma db member(location) column("") \ + set(this.location = std::move (?); \ + assert (this.name == this.location.canonical_name ())) + + #pragma db member(fragments) id_column("repository") \ + value_column("") value_not_null + + private: + friend class odb::access; repository () = default; }; @@ -300,18 +386,8 @@ namespace bpkg #pragma db value struct package_location { - using repository_type = bpkg::repository; - - // Fragment is optional, repository type-specific information that can be - // used to identify the repository fragment/partition/view/etc that this - // package came from. For example, for a version control-based repository - // this could be a commit id. - // - // The location is the package location within this repository fragment. - // - lazy_shared_ptr repository; - string fragment; - path location; + lazy_shared_ptr repository_fragment; + path location; // Package location within the repository fragment. }; // dependencies @@ -361,20 +437,21 @@ namespace bpkg available_package_id id; upstream_version version; - // List of repositories to which this package version belongs (yes, - // in our world, it can be in multiple, unrelated repositories). + // List of repository fragments to which this package version belongs + // (yes, in our world, it can be in multiple, unrelated repositories) + // together with locations within these repository fragments. // - // Note that if the repository is the special root repository (its - // location is empty), then this is a transient (or "fake") object - // for an existing package archive or package directory. In this - // case the location is the path to the archive/directory and to - // determine which one it is, use file/dir_exists(). While on the - // topic of fake available_package objects, when one is created for - // a selected package (see make_available()), this list is left empty - // with the thinking being that since the package is already in at - // least fetched state, we shouldn't be needing its location. + // Note that if the entry is the special root repository fragment (its + // location is empty), then this is a transient (or "fake") object for an + // existing package archive or package directory. In this case the + // location is the path to the archive/directory and to determine which + // one it is, use file/dir_exists(). While on the topic of fake + // available_package objects, when one is created for a selected package + // (see make_available()), this list is left empty with the thinking being + // that since the package is already in at least fetched state, we + // shouldn't be needing its location. // - vector locations; //@@ Map? + vector locations; // Package manifest data. // @@ -496,28 +573,28 @@ namespace bpkg operator size_t () const {return result;} }; - // Only return packages that are in the specified repositories, their + // Only return packages that are in the specified repository fragments, their // complements or prerequisites (if prereq is true), recursively. While you // could maybe come up with a (barely comprehensible) view/query to achieve // this, doing it on the "client side" is definitely more straightforward. // vector> - filter (const shared_ptr&, + filter (const shared_ptr&, odb::result&&, bool prereq = true); - pair, shared_ptr> - filter_one (const shared_ptr&, + pair, shared_ptr> + filter_one (const shared_ptr&, odb::result&&, bool prereq = true); - shared_ptr - filter (const shared_ptr&, + shared_ptr + filter (const shared_ptr&, const shared_ptr&, bool prereq = true); - vector, shared_ptr>> - filter (const vector>&, + vector, shared_ptr>> + filter (const vector>&, odb::result&&, bool prereq = true); @@ -606,41 +683,38 @@ namespace bpkg package_state state; package_substate substate; - // The hold flags indicate whether this package and/or version - // should be retained in the configuration. A held package will - // not be automatically removed. A held version will not be - // automatically upgraded. Note also that the two flags are - // orthogonal: we may want to keep a specific version of the - // package as long as it has dependents. + // The hold flags indicate whether this package and/or version should be + // retained in the configuration. A held package will not be automatically + // removed. A held version will not be automatically upgraded. Note also + // that the two flags are orthogonal: we may want to keep a specific + // version of the package as long as it has dependents. // bool hold_package; bool hold_version; - // Repository from which this package came. Note that it is not - // a pointer to the repository object because it could be wiped - // out (e.g., as a result of rep-fetch). We call such packages - // "orphans". While we can get a list of orphan's prerequisites - // (by loading its manifest), we wouldn't know which repository - // to use as a base to resolve them. As a result, an orphan that - // is not already configured (and thus has all its prerequisites - // resolved) is not very useful and can only be purged. + // Repository fragment from which this package came. Note that it is not a + // pointer to the repository_fragment object because it could be wiped out + // (e.g., as a result of rep-fetch). We call such packages "orphans". + // While we can get a list of orphan's prerequisites (by loading its + // manifest), we wouldn't know which repository fragment to use as a base + // to resolve them. As a result, an orphan that is not already configured + // (and thus has all its prerequisites resolved) is not very useful and + // can only be purged. // - repository_location repository; + repository_location repository_fragment; - // Path to the archive of this package, if any. If not absolute, - // then it is relative to the configuration directory. The purge - // flag indicates whether the archive should be removed when the - // packaged is purged. If the archive is not present, it should - // be false. + // Path to the archive of this package, if any. If not absolute, then it + // is relative to the configuration directory. The purge flag indicates + // whether the archive should be removed when the packaged is purged. If + // the archive is not present, it should be false. // optional archive; bool purge_archive; - // Path to the source directory of this package, if any. If not - // absolute, then it is relative to the configuration directory. - // The purge flag indicates whether the directory should be - // removed when the packaged is purged. If the source directory - // is not present, it should be false. + // Path to the source directory of this package, if any. If not absolute, + // then it is relative to the configuration directory. The purge flag + // indicates whether the directory should be removed when the packaged is + // purged. If the source directory is not present, it should be false. // optional src_root; bool purge_src; @@ -683,11 +757,12 @@ namespace bpkg return // pkg-unpack / // - (!repository.empty () && repository.directory_based ()) || + (!repository_fragment.empty () && + repository_fragment.directory_based ()) || // pkg-unpack --existing // - (repository.empty () && !archive); + (repository_fragment.empty () && !archive); } // Represent the wildcard version with the "*" string. Represent naturally @@ -899,45 +974,66 @@ namespace bpkg optional constraint; }; - // Return a list of repositories that depend on this repository as a - // complement. + // Return a count of repositories that contain this repository fragment. // - // Note that the last db object pragma is required to produce an object - // loading view. + #pragma db view table("repository_fragments") + struct fragment_repository_count + { + #pragma db column("count(*)") + size_t result; + + operator size_t () const {return result;} + }; + + // Return a list of repositories that contain this repository fragment. // - #pragma db view object(repository = complement) \ - table("repository_complements" = "rc" inner: \ - "rc.complement = " + complement::name) \ - object(repository inner: "rc.repository = " + repository::name) - struct repository_complement_dependent + #pragma db view object(repository) \ + table("repository_fragments" = "rfs" inner: \ + "rfs.repository = " + repository::name) \ + object(repository_fragment inner: "rfs.fragment = " + \ + repository_fragment::name) + struct fragment_repository { shared_ptr object; operator const shared_ptr () const {return object;} }; - // Return a list of repositories that depend on this repository as a - // prerequisite. + // Return a list of repository fragments that depend on this repository as a + // complement. // - // Note that the last db object pragma is required to produce an object - // loading view. + #pragma db view object(repository = complement) \ + table("repository_fragment_complements" = "rfc" inner: \ + "rfc.complement = " + complement::name) \ + object(repository_fragment inner: "rfc.repository_fragment = " + \ + repository_fragment::name) + struct repository_complement_dependent + { + shared_ptr object; + + operator const shared_ptr () const {return object;} + }; + + // Return a list of repository fragments that depend on this repository as a + // prerequisite. // - #pragma db view object(repository = prerequisite) \ - table("repository_prerequisites" = "rp" inner: \ - "rp.prerequisite = " + prerequisite::name) \ - object(repository inner: "rp.repository = " + repository::name) + #pragma db view object(repository = prerequisite) \ + table("repository_fragment_prerequisites" = "rfp" inner: \ + "rfp.prerequisite = " + prerequisite::name) \ + object(repository_fragment inner: "rfp.repository_fragment = " + \ + repository_fragment::name) struct repository_prerequisite_dependent { - shared_ptr object; + shared_ptr object; - operator const shared_ptr () const {return object;} + operator const shared_ptr () const {return object;} }; - // Return a list of packages available from this repository. + // Return a list of packages available from this repository fragment. // - #pragma db view object(repository) query(distinct) \ + #pragma db view object(repository_fragment) \ table("available_package_locations" = "pl" inner: \ - "pl.repository = " + repository::name) \ + "pl.repository_fragment = " + repository_fragment::name) \ object(available_package = package inner: \ "pl.name = " + package::id.name + "AND" + \ "pl.version_epoch = " + package::id.version.epoch + "AND" + \ @@ -947,18 +1043,18 @@ namespace bpkg package::id.version.canonical_release + "AND" + \ "pl.version_revision = " + package::id.version.revision + "AND" + \ "pl.version_iteration = " + package::id.version.iteration) - struct repository_package + struct repository_fragment_package { shared_ptr package; // Must match the alias (see above). operator const shared_ptr () const {return package;} }; - // Return a list of repositories the packages come from. + // Return a list of repository fragments the packages come from. // - #pragma db view object(repository) query(distinct) \ + #pragma db view object(repository_fragment) \ table("available_package_locations" = "pl" inner: \ - "pl.repository = " + repository::name) \ + "pl.repository_fragment = " + repository_fragment::name) \ object(available_package = package inner: \ "pl.name = " + package::id.name + "AND" + \ "pl.version_epoch = " + package::id.version.epoch + "AND" + \ @@ -968,13 +1064,12 @@ namespace bpkg package::id.version.canonical_release + "AND" + \ "pl.version_revision = " + package::id.version.revision + "AND" + \ "pl.version_iteration = " + package::id.version.iteration) - struct package_repository + struct package_repository_fragment { #pragma db column(package::id) available_package_id package_id; - using repository_type = bpkg::repository; - shared_ptr repository; + shared_ptr repository_fragment; }; // Version comparison operators. -- cgit v1.1