From 5d513688ae07d96910dd1eef83bdad4e9d780373 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Thu, 22 Apr 2021 21:57:13 +0300 Subject: Add support for linked configurations --- bpkg/package.hxx | 305 +++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 230 insertions(+), 75 deletions(-) (limited to 'bpkg/package.hxx') diff --git a/bpkg/package.hxx b/bpkg/package.hxx index cee2dd6..a0e809a 100644 --- a/bpkg/package.hxx +++ b/bpkg/package.hxx @@ -18,7 +18,7 @@ #include #include -#include // transaction +#include // database, transaction #include #include @@ -27,23 +27,12 @@ // #define DB_SCHEMA_VERSION_BASE 6 -#pragma db model version(DB_SCHEMA_VERSION_BASE, 8, closed) +#pragma db model version(DB_SCHEMA_VERSION_BASE, 9, closed) namespace bpkg { - // Compare two lazy pointers via the pointed-to object ids. - // - struct compare_lazy_ptr - { - template - bool - operator() (const P& x, const P& y) const - { - return x.object_id () < y.object_id (); - } - }; - using optional_string = optional; + using optional_uint64_t = optional; // Preserve uint64_t alias. // path // @@ -67,6 +56,10 @@ namespace bpkg to((?) ? (?)->string () : bpkg::optional_string ()) \ from((?) ? bpkg::dir_path (*(?)) : bpkg::optional_dir_path ()) + // uuid + // + #pragma db map type(uuid) as(string) to((?).string ()) from(bpkg::uuid (?)) + // timestamp // using butl::timestamp; @@ -122,8 +115,6 @@ namespace bpkg #include -#include - // Prevent assert() macro expansion in get/set expressions. This should // appear after all #include directives since the assert() macro is // redefined in each inclusion. @@ -136,6 +127,97 @@ void assert (int); namespace bpkg { + // Linked bpkg configuration. + // + // Link with id 0 is the special self-link which captures information about + // the current configuration. This information is cached in links of other + // configurations. + // + // Note that linked configurations information will normally be accessed + // through the database object functions, which load and cache this + // information on the first call. This makes the session support for the + // configuration class redundant. Moreover, with the session support + // disabled the database implementation can freely move out the data from + // the configuration objects into the internal cache and safely load them + // from the temporary database objects (see database::attach() for details). + // + #pragma db object pointer(shared_ptr) + class configuration + { + public: + using uuid_type = bpkg::uuid; + + // Link id. + // + // Zero for the self-link and is auto-assigned for linked configurations + // when the object is persisted. + // + optional_uint64_t id; // Object id. + + uuid_type uuid; + optional name; + string type; + dir_path path; // Empty for the self-link. + + // True if the link is created explicitly by the user rather than + // automatically as a back-link. + // + bool expl; + + // Database mapping. + // + #pragma db member(id) id auto + #pragma db member(uuid) unique + #pragma db member(name) unique + #pragma db member(path) unique + #pragma db member(expl) column("explicit") + + public: + // Create the self-link. Generate the UUID, unless specified. + // + configuration (optional n, + string t, + optional uid = nullopt); + + // Create a linked configuration. + // + configuration (const uuid_type& uid, + optional n, + string t, + dir_path p, + bool e) + : uuid (uid), + name (move (n)), + type (move (t)), + path (move (p)), + expl (e) {} + + // If the configuration path is absolute, then return it as is. Otherwise, + // return it completed relative to the specified linked configuration + // directory path and then normalized. The specified directory path should + // be absolute and normalized. Issue diagnostics and fail on the path + // conversion error. + // + // Note that the self-link object is naturally supported by this function, + // since its path is empty. + // + dir_path + effective_path (const dir_path&) const; + + const dir_path& + make_effective_path (const dir_path& d) + { + if (path.relative ()) + path = effective_path (d); + + return path; + } + + private: + friend class odb::access; + configuration () = default; + }; + // version // // Sometimes we need to split the version into two parts: the part @@ -328,7 +410,8 @@ namespace bpkg // // Also note that these point to repositories, not repository fragments. // - using dependencies = std::set, compare_lazy_ptr>; + using dependencies = std::set, + compare_lazy_ptr_id>; dependencies complements; dependencies prerequisites; @@ -532,9 +615,6 @@ namespace bpkg available_package_id (package_name, const bpkg::version&); }; - bool - operator< (const available_package_id&, const available_package_id&); - #pragma db object pointer(shared_ptr) session class available_package { @@ -612,48 +692,13 @@ namespace bpkg // we do not implicitly assume a wildcard version. // const version_type* - system_version () const - { - if (!system_version_) - { - if (const system_package* sp = system_repository.find (id.name)) - { - // Only cache if it is authoritative. - // - if (sp->authoritative) - system_version_ = sp->version; - else - return &sp->version; - } - } - - return system_version_ ? &*system_version_ : nullptr; - } + system_version (database&) const; // As above but also return an indication if the version information is // authoritative. // pair - system_version_authoritative () const - { - const system_package* sp (system_repository.find (id.name)); - - if (!system_version_) - { - if (sp != nullptr) - { - // Only cache if it is authoritative. - // - if (sp->authoritative) - system_version_ = sp->version; - else - return make_pair (&sp->version, false); - } - } - - return make_pair (system_version_ ? &*system_version_ : nullptr, - sp != nullptr ? sp->authoritative : false); - } + system_version_authoritative (database&) const; // Database mapping. // @@ -785,9 +830,7 @@ namespace bpkg // NULL, print the error message and fail. // void - check_any_available (const dir_path& configuration, - transaction&, - const diag_record* = nullptr); + check_any_available (database&, transaction&, const diag_record* = nullptr); // package_state // @@ -862,17 +905,59 @@ namespace bpkg const optional&, bool system = false); + // Return true if the package is a build2 build system module. + // + inline bool + build2_module (const package_name& name) + { + return name.string ().compare (0, 10, "libbuild2-") == 0; + } + // A map of "effective" prerequisites (i.e., pointers to other selected // packages) to optional version constraint. Note that because it is a // single constraint, we don't support multiple dependencies on the same // package (e.g., two ranges of versions). See pkg_configure(). // + // Note also that the pointer can refer to a selected package in another + // database. + // class selected_package; + // Note that the keys for this map need to be created with the database + // passed to their constructor, which is required for persisting them (see + // _selected_package_ref() implementation for details). + // using package_prerequisites = std::map, optional, compare_lazy_ptr>; + // Database mapping for lazy_shared_ptr to configuration + // UUID and package name. + // + #pragma db value + struct _selected_package_ref + { + using ptr = lazy_shared_ptr; + + uuid configuration; + package_name prerequisite; + + explicit + _selected_package_ref (const ptr&); + + _selected_package_ref () = default; + + ptr + to_ptr (odb::database&) &&; + + #pragma db member(configuration) + }; + + #pragma db map type(_selected_package_ref::ptr) \ + as(_selected_package_ref) \ + to(bpkg::_selected_package_ref (?)) \ + from(std::move (?).to_ptr (*db)) + #pragma db object pointer(shared_ptr) session class selected_package { @@ -971,11 +1056,17 @@ namespace bpkg // all other versions. // std::string - version_string () const; + version_string () const + { + return version != wildcard_version ? version.string () : "*"; + } std::string string () const {return package_string (name, version, system ());} + std::string + string (database&) const; + // Return the relative source directory completed using the configuration // directory. Return the absolute source directory as is. // @@ -1004,8 +1095,8 @@ namespace bpkg // #pragma db member(name) id - #pragma db member(prerequisites) id_column("package") \ - key_column("prerequisite") key_not_null value_column("") + #pragma db member(prerequisites) id_column("package") \ + key_column("") value_column("") // Explicit aggregate initialization for C++20 (private default ctor). // @@ -1049,6 +1140,15 @@ namespace bpkg return os << p.string (); } + // Try to find a dependency in the dependency configurations (see + // database::dependency_configs() for details). Return pointers to the found + // package and the configuration it belongs to. Return a pair of NULLs if no + // package is found and issue diagnostics and fail if multiple packages (in + // multiple configurations) are found. + // + pair, database*> + find_dependency (database&, const package_name&, bool buildtime); + // Check if the directory containing the specified package version should be // considered its iteration. Return the version of this iteration if that's // the case and nullopt otherwise. @@ -1081,9 +1181,11 @@ namespace bpkg // class common_options; + // Note: loads selected packages. + // optional package_iteration (const common_options&, - const dir_path& configuration, + database&, transaction&, const dir_path&, const package_name&, @@ -1179,25 +1281,22 @@ namespace bpkg // Return a list of packages that depend on this package along with // their constraints. // + // @@ Using raw container table since ODB doesn't support containers in + // views yet. + // /* - #pragma db view object(selected_package) \ - container(selected_package::prerequisites = pp inner: pp.key) + #pragma db view container(selected_package::prerequisites = pp) struct package_dependent { - #pragma db column(pp.id) - string name; + #pragma db column("pp.package") + package_name name; - #pragma db column(pp.value) + #pragma db column("pp.") optional constraint; }; */ - // @@ Using raw container table since ODB doesn't support containers - // in views yet. - // - #pragma db view object(selected_package) \ - table("main.selected_package_prerequisites" = "pp" inner: \ - "pp.prerequisite = " + selected_package::name) + #pragma db view table("main.selected_package_prerequisites" = "pp") struct package_dependent { #pragma db column("pp.package") @@ -1207,6 +1306,55 @@ namespace bpkg optional constraint; }; + // In the specified database query dependents of a dependency that resided + // in a potentially different database (yeah, it's a mouthful). + // + odb::result + query_dependents (database& dependent_db, + const package_name& dependency, + database& dependency_db); + + // Database and package name pair. + // + // It is normally used as a key for maps containing data for packages across + // multiple linked configurations. Assumes that the respective databases are + // not detached during such map lifetimes. Considers both package name and + // database for objects comparison. + // + struct config_package + { + database& db; + package_name name; + + config_package (database& d, package_name n): db (d), name (move (n)) {} + + // Create a pseudo-package (command line as a dependent, etc). + // + config_package (database& d, string n) + : db (d), + name (n.empty () ? package_name () : package_name (move (n))) {} + + bool + operator== (const config_package& v) const + { + // See operator==(database, database). + // + return name == v.name && &db == &v.db; + } + + bool + operator< (const config_package& v) const + { + // See operator==(database, database). + // + int r (name.compare (v.name)); + return r != 0 ? (r < 0) : (&db < &v.db); + } + + std::string + string () const; + }; + // Return a count of repositories that contain this repository fragment. // #pragma db view table("main.repository_fragments") @@ -1472,6 +1620,13 @@ namespace bpkg } */ + inline bool + operator< (const available_package_id& x, const available_package_id& y) + { + int r (x.name.compare (y.name)); + return r != 0 ? r < 0 : x.version < y.version; + } + template inline auto compare_version_gt (const T1& x, const T2& y, bool revision, bool iteration) -- cgit v1.1