diff options
Diffstat (limited to 'bpkg/package.hxx')
-rw-r--r-- | bpkg/package.hxx | 850 |
1 files changed, 682 insertions, 168 deletions
diff --git a/bpkg/package.hxx b/bpkg/package.hxx index cee2dd6..8fe448c 100644 --- a/bpkg/package.hxx +++ b/bpkg/package.hxx @@ -11,39 +11,33 @@ #include <type_traits> // static_assert #include <odb/core.hxx> +#include <odb/section.hxx> #include <odb/nested-container.hxx> -#include <libbutl/timestamp.mxx> +#include <libbutl/timestamp.hxx> #include <libbpkg/package-name.hxx> #include <bpkg/types.hxx> -#include <bpkg/forward.hxx> // transaction +#include <bpkg/forward.hxx> // database, linked_databases, transaction #include <bpkg/utility.hxx> #include <bpkg/diagnostics.hxx> // Used by the data migration entries. // -#define DB_SCHEMA_VERSION_BASE 6 +// NOTE: drop all the `#pragma db member(...) default(...)` pragmas when +// migration is no longer supported (i.e., the current and base schema +// versions are the same). +// +#define DB_SCHEMA_VERSION_BASE 23 -#pragma db model version(DB_SCHEMA_VERSION_BASE, 8, closed) +#pragma db model version(DB_SCHEMA_VERSION_BASE, 26, closed) namespace bpkg { - // Compare two lazy pointers via the pointed-to object ids. - // - struct compare_lazy_ptr - { - template <typename P> - bool - operator() (const P& x, const P& y) const - { - return x.object_id () < y.object_id (); - } - }; - using optional_string = optional<string>; + using optional_uint64_t = optional<uint64_t>; // Preserve uint64_t alias. // path // @@ -67,6 +61,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; @@ -80,7 +78,7 @@ namespace bpkg std::chrono::nanoseconds::period>::value, "The following timestamp ODB mapping is invalid"); - // As pointed out in libbutl/timestamp.mxx we will overflow in year 2262, but + // As pointed out in libbutl/timestamp.hxx we will overflow in year 2262, but // by that time some larger basic type will be available for mapping. // #pragma db map type(timestamp) as(uint64_t) \ @@ -106,7 +104,7 @@ namespace bpkg string upstream; optional<string> release; - // @@ TMP: work around MSVC 16.2 bug. + // Work around MSVC 16.2 bug. // _version () = default; _version (uint16_t e, @@ -122,8 +120,6 @@ namespace bpkg #include <libbpkg/manifest.hxx> -#include <bpkg/system-repository.hxx> - // Prevent assert() macro expansion in get/set expressions. This should // appear after all #include directives since the assert() macro is // redefined in each <assert.h> inclusion. @@ -136,6 +132,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<string> 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 backlink. + // + 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<string> n, + string t, + optional<uuid_type> uid = nullopt); + + // Create a linked configuration. + // + configuration (const uuid_type& uid, + optional<string> 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 @@ -261,7 +348,7 @@ namespace bpkg repository_url url; repository_type type; - // @@ TMP: work around MSVC 16.2 bug. + // Work around MSVC 16.2 bug. // _repository_location () = default; _repository_location (repository_url u, repository_type t) @@ -328,7 +415,8 @@ namespace bpkg // // Also note that these point to repositories, not repository fragments. // - using dependencies = std::set<lazy_weak_ptr<repository>, compare_lazy_ptr>; + using dependencies = std::set<lazy_weak_ptr<repository>, + compare_lazy_ptr_id>; dependencies complements; dependencies prerequisites; @@ -430,6 +518,10 @@ namespace bpkg operator size_t () const {return result;} }; + // language + // + #pragma db value(language) definition + // package_location // #pragma db value @@ -452,6 +544,7 @@ namespace bpkg #pragma db value(version_constraint) definition #pragma db value(dependency) definition #pragma db member(dependency::constraint) column("") + #pragma db value(dependency_alternative) definition #pragma db value(dependency_alternatives) definition // Extend dependency_alternatives to also represent the special test @@ -472,12 +565,15 @@ namespace bpkg dependency_alternatives_ex (dependency_alternatives da) : dependency_alternatives (move (da)) {} + // As above but built incrementally. + // + dependency_alternatives_ex (bool b, std::string c) + : dependency_alternatives (b, move (c)) {} + // Create the special test dependencies object (built incrementally). // - dependency_alternatives_ex (test_dependency_type t) - : dependency_alternatives (false /* conditional */, - false /* buildtime */, - "" /* comment */), + dependency_alternatives_ex (test_dependency_type t, bool buildtime) + : dependency_alternatives (buildtime, "" /* comment */), type (t) {} }; @@ -493,10 +589,41 @@ namespace bpkg make_move_iterator (das.end ())); } + // Return true if this is a toolchain build-time dependency. If the package + // argument is specified and this is a toolchain build-time dependency then + // also verify its constraint and fail if it is unsatisfied. Note that the + // package argument is used for diagnostics only. + // + class common_options; + + bool + toolchain_buildtime_dependency (const common_options&, + const dependency_alternatives&, + const package_name*); + + // Return true if any dependency other than toolchain build-time + // dependencies is specified. Optionally, verify toolchain build-time + // dependencies specifying the package argument which will be used for + // diagnostics only. + // + bool + has_dependencies (const common_options&, + const dependencies&, + const package_name* = nullptr); + + // Return true if some clause that is a buildfile fragment is specified for + // any of the dependencies. + // + template <typename T> + bool + has_buildfile_clause (const vector<T>& dependencies); + // tests // #pragma db value(test_dependency) definition + #pragma db member(test_dependency::buildtime) default(false) + using optional_test_dependency_type = optional<test_dependency_type>; #pragma db map type(test_dependency_type) as(string) \ @@ -516,6 +643,19 @@ namespace bpkg // extern const version wildcard_version; + // Return true if the version constraint represents the wildcard version. + // + inline bool + wildcard (const version_constraint& vc) + { + bool r (vc.min_version && *vc.min_version == wildcard_version); + + if (r) + assert (vc.max_version == vc.min_version); + + return r; + } + // package_name // #pragma db value(package_name) type("TEXT") options("COLLATE NOCASE") @@ -532,17 +672,31 @@ namespace bpkg available_package_id (package_name, const bpkg::version&); }; - bool - operator< (const available_package_id&, const available_package_id&); + // buildfile + // + #pragma db value(buildfile) definition + + // distribution_name_value + // + #pragma db value(distribution_name_value) definition #pragma db object pointer(shared_ptr) session class available_package { public: using version_type = bpkg::version; + using upstream_version_type = bpkg::upstream_version; available_package_id id; - upstream_version version; + upstream_version_type version; + + optional<string> upstream_version; + optional<string> type; + + small_vector<language, 1> languages; + odb::section languages_section; + + optional<package_name> project; // List of repository fragments to which this package version belongs // (yes, in our world, it can be in multiple, unrelated repositories) @@ -562,8 +716,17 @@ namespace bpkg // Package manifest data and, potentially, the special test dependencies. // - // Note that there can be only one special test dependencies entry in the - // list and it's always the last one, if present. + // Note that there can only be one special test dependencies entry in the + // list. It can only be present for a test package and specifies all the + // main packages as the alternative dependencies. If present, it is + // located right after the last explicit depends clause which specifies a + // main package for this test package, if such a clause is present, and as + // the first entry otherwise. The idea here is to inject the special + // depends clause as early as possible, so that the other clauses could + // potentially refer to the reflection variables it may set. But not too + // early, so that the explicit main package dependencies are already + // resolved by the time of resolving the special clause to avoid the + // 'unable to select dependency alternative' error. // using dependencies_type = bpkg::dependencies; @@ -571,6 +734,18 @@ namespace bpkg small_vector<test_dependency, 1> tests; + // Note that while the bootstrap buildfile is always present for stub + // packages, we don't save buildfiles for stubs of any kind (can come from + // repository, be based on system selected package, etc), leaving *_build + // as nullopt and buildfiles empty. + // + optional<bool> alt_naming; + optional<string> bootstrap_build; + optional<string> root_build; + vector<buildfile> buildfiles; + + vector<distribution_name_value> distribution_values; + // Present for non-transient objects only (and only for certain repository // types). // @@ -581,14 +756,31 @@ namespace bpkg mutable optional<version_type> system_version_; public: - // Note: version constraints must be complete. + // Note: version constraints must be complete and the bootstrap build must + // be present, unless this is a stub. // available_package (package_manifest&& m) : id (move (m.name), m.version), version (move (m.version)), + upstream_version (move (m.upstream_version)), + type (move (m.type)), + languages (move (m.languages)), + project (move (m.project)), dependencies (convert (move (m.dependencies))), tests (move (m.tests)), - sha256sum (move (m.sha256sum)) {} + distribution_values (move (m.distribution_values)), + sha256sum (move (m.sha256sum)) + { + if (!stub ()) + { + assert (m.bootstrap_build.has_value () && m.alt_naming.has_value ()); + + alt_naming = m.alt_naming; + bootstrap_build = move (m.bootstrap_build); + root_build = move (m.root_build); + buildfiles = move (m.buildfiles); + } + } // Create available stub package. // @@ -608,88 +800,139 @@ namespace bpkg bool stub () const {return version.compare (wildcard_version, true) == 0;} + string + effective_type () const + { + return package_manifest::effective_type (type, id.name); + } + + small_vector<language, 1> + effective_languages () const + { + return package_manifest::effective_languages (languages, id.name); + } + // Return package system version if one has been discovered. Note that // 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<const version_type*, bool> - 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. // #pragma db member(id) id column("") #pragma db member(version) set(this.version.init (this.id.version, (?))) + + // languages + // + #pragma db member(languages) id_column("") value_column("language_") \ + section(languages_section) + + #pragma db member(languages_section) load(lazy) update(always) + + // locations + // #pragma db member(locations) id_column("") value_column("") \ unordered value_not_null // dependencies // - using _dependency_key = odb::nested_key<dependency_alternatives_ex>; - using _dependency_alternatives_ex_type = - std::map<_dependency_key, dependency>; - - #pragma db value(_dependency_key) - #pragma db member(_dependency_key::outer) column("dependency_index") - #pragma db member(_dependency_key::inner) column("index") + // Note that this is a 2-level nested container which is mapped to three + // container tables each containing data of each dimension. + // Container of the dependency_alternatives_ex values. + // #pragma db member(dependencies) id_column("") value_column("") - #pragma db member(dependency_alternatives_ex) \ - table("available_package_dependency_alternatives") \ - virtual(_dependency_alternatives_ex_type) \ + + // Container of the dependency_alternative values. + // + using _dependency_alternative_key = + odb::nested_key<dependency_alternatives_ex>; + + using _dependency_alternatives_type = + std::map<_dependency_alternative_key, dependency_alternative>; + + #pragma db value(_dependency_alternative_key) + #pragma db member(_dependency_alternative_key::outer) column("dependency_index") + #pragma db member(_dependency_alternative_key::inner) column("index") + + #pragma db member(dependency_alternatives) \ + virtual(_dependency_alternatives_type) \ after(dependencies) \ get(odb::nested_get (this.dependencies)) \ set(odb::nested_set (this.dependencies, std::move (?))) \ + id_column("") key_column("") value_column("") + + // Container of the dependency values. + // + using _dependency_key = odb::nested2_key<dependency_alternatives_ex>; + using _dependency_alternative_dependencies_type = + std::map<_dependency_key, dependency>; + + #pragma db value(_dependency_key) + #pragma db member(_dependency_key::outer) column("dependency_index") + #pragma db member(_dependency_key::middle) column("alternative_index") + #pragma db member(_dependency_key::inner) column("index") + + #pragma db member(dependency_alternative_dependencies) \ + virtual(_dependency_alternative_dependencies_type) \ + after(dependency_alternatives) \ + get(odb::nested2_get (this.dependencies)) \ + set(odb::nested2_set (this.dependencies, std::move (?))) \ id_column("") key_column("") value_column("dep_") // tests // #pragma db member(tests) id_column("") value_column("test_") + // distribution_values + // + #pragma db member(distribution_values) id_column("") value_column("dist_") + + // alt_naming + // + // Note that since no real packages with alternative buildfile naming use + // conditional dependencies yet, we can just set alt_naming to false + // during migration to the database schema version 20. Also we never rely + // on alt_naming to be nullopt for the stub packages, so let's not + // complicate things and set alt_naming to false for them either. + // + #pragma db member(alt_naming) default(false) + + // *_build + // + // Note that since no real packages use conditional dependencies yet, we + // can just set bootstrap_build to the empty string during migration to + // the database schema version 15. Also we never rely on bootstrap_build + // to be nullopt for the stub packages, so let's not complicate things and + // set bootstrap_build to the empty string for them either. + // + #pragma db member(bootstrap_build) default("") + + // buildfiles + // + #pragma db member(buildfiles) id_column("") value_column("") + private: friend class odb::access; available_package () = default; }; + // The available packages together with the repository fragments they belong + // to. + // + // Note that lazy_shared_ptr is used to also convey the databases the + // objects belong to. + // + using available_packages = vector<pair<shared_ptr<available_package>, + lazy_shared_ptr<repository_fragment>>>; + #pragma db view object(available_package) struct available_package_count { @@ -740,55 +983,18 @@ namespace bpkg shared_ptr<available_package> package; }; - // Query the available packages that optionally satisfy the specified - // version constraint and return them in the version descending order, by - // default. Note that a stub satisfies any constraint. - // - odb::result<available_package> - query_available (database&, - const package_name&, - const optional<version_constraint>&, - bool order = true); - - // 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<shared_ptr<available_package>> - filter (const shared_ptr<repository_fragment>&, - odb::result<available_package>&&, - bool prereq = true); - - pair<shared_ptr<available_package>, shared_ptr<repository_fragment>> - filter_one (const shared_ptr<repository_fragment>&, - odb::result<available_package>&&, - bool prereq = true); - - shared_ptr<repository_fragment> - filter (const shared_ptr<repository_fragment>&, - const shared_ptr<available_package>&, - bool prereq = true); - - vector<pair<shared_ptr<available_package>, shared_ptr<repository_fragment>>> - filter (const vector<shared_ptr<repository_fragment>>&, - odb::result<available_package>&&, - bool prereq = true); - - pair<shared_ptr<available_package>, shared_ptr<repository_fragment>> - filter_one (const vector<shared_ptr<repository_fragment>>&, - odb::result<available_package>&&, - bool prereq = true); - - // Check if there are packages available in the configuration. If that's not - // the case then print the info message into the diag record or, if it is - // NULL, print the error message and fail. + // Check if there are packages available in the specified configurations. If + // that's not the case then print the info message into the diag record or, + // if it is NULL, print the error message and fail. // void - check_any_available (const dir_path& configuration, + check_any_available (const linked_databases&, transaction&, const diag_record* = nullptr); + void + check_any_available (database&, transaction&, const diag_record* = nullptr); + // package_state // enum class package_state @@ -862,17 +1068,97 @@ namespace bpkg const optional<version_constraint>&, 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(). + // packages) to optional version constraint (plus some other info). 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; + #pragma db value + struct prerequisite_info + { + // The "tightest" version constraint among all dependencies resolved to + // this prerequisite. + // + optional<version_constraint> constraint; + + // Database mapping. + // + #pragma db member(constraint) column("") + }; + + // 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<lazy_shared_ptr<selected_package>, - optional<version_constraint>, + prerequisite_info, compare_lazy_ptr>; + // Database mapping for lazy_shared_ptr<selected_package> to configuration + // UUID and package name. + // + #pragma db value + struct _selected_package_ref + { + using ptr = lazy_shared_ptr<selected_package>; + + 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)) + + enum class config_source + { + user, // User configuration specified on command line. + dependent, // Dependent-imposed configuration from prefer/require clauses. + reflect // Package-reflected configuration from reflect clause. + }; + + string + to_string (config_source); + + config_source + to_config_source (const string&); // May throw std::invalid_argument. + + #pragma db map type(config_source) as(string) \ + to(to_string (?)) \ + from(bpkg::to_config_source (?)) + + #pragma db value + struct config_variable + { + string name; + config_source source; + }; + #pragma db object pointer(shared_ptr) session class selected_package { @@ -920,27 +1206,65 @@ namespace bpkg optional<dir_path> src_root; bool purge_src; - // The checksum of the manifest file located in the source directory. + // The checksum of the manifest file located in the source directory and + // the subproject set. Changes to this information should trigger the + // package version revision increment. In particular, new subprojects + // should trigger the package reconfiguration. // - // Must be present if the source directory is present, unless the object - // is created/updated during the package build simulation (see pkg-build - // for details). Note that during the simulation the manifest may not be + // Only present for external packages, unless the objects are + // created/updated during the package build simulation (see pkg-build for + // details). Note that during the simulation the manifest may not be // available. // + // @@ Currently we don't consider subprojects recursively (would most + // likely require extension to b info, also could be a performance + // concern). + // + // @@ We should probably rename it if/when ODB add support for that for + // SQlite. + // optional<std::string> manifest_checksum; - // Path to the output directory of this package, if any. It is - // always relative to the configuration directory, and is <name> - // for external packages and <name>-<version> for others. It is - // only set once the package is configured and its main purse is - // to keep track of what needs to be cleaned by the user before - // a broken package can be purged. Note that it could be the - // same as src_root. + // Only present for external packages which have buildfile clauses in the + // dependencies, unless the objects are created/updated during the package + // build simulation (see pkg-build for details). + // + // Note that the checksum is always calculated over the files rather than + // the *-build manifest values. This is "parallel" to the package skeleton + // logic. + // + optional<std::string> buildfiles_checksum; + + // Path to the output directory of this package, if any. It is always + // relative to the configuration directory, and is <name> for external + // packages and <name>-<version> for others. It is only set once the + // package is configured and its main purpose is to keep track of what + // needs to be cleaned by the user before a broken package can be + // purged. Note that it could be the same as src_root. // optional<dir_path> out_root; package_prerequisites prerequisites; + // 1-based indexes of the selected dependency alternatives which the + // prerequisite packages are resolved from. Parallel to the dependencies + // member of the respective available package. Entries which don't + // correspond to a selected alternative (toolchain build-time dependency, + // not enabled alternatives, etc) are set to 0. + // + using indexes_type = vector<size_t>; // Make sure ODB maps it portably. + indexes_type dependency_alternatives; + odb::section dependency_alternatives_section; + + // Project configuration variable names and their sources. + // + vector<config_variable> config_variables; + + // SHA256 checksum of variables (names and values) referred to by the + // config_variables member. + // + std::string config_checksum; + public: bool system () const @@ -964,18 +1288,39 @@ namespace bpkg // pkg-unpack --existing <dir> // - (repository_fragment.empty () && !archive); + // Note that the system package can have no repository associated (see + // imaginary system repository in pkg-build.cxx for details). + // + (repository_fragment.empty () && !archive && !system ()); } // Represent the wildcard version with the "*" string. Represent naturally // 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 archive path completed using the configuration + // directory. Return the absolute archive path as is. + // + path + effective_archive (const dir_path& configuration) const + { + // Cast for compiling with ODB (see above). + // + assert (static_cast<bool> (archive)); + return archive->absolute () ? *archive : configuration / *archive; + } + // Return the relative source directory completed using the configuration // directory. Return the absolute source directory as is. // @@ -988,8 +1333,7 @@ namespace bpkg return src_root->absolute () ? *src_root : configuration / *src_root; } - // Return the output directory using the configuration directory. Note - // that the output directory is always relative. + // Return the output directory using the configuration directory. // dir_path effective_out_root (const dir_path& configuration) const @@ -997,6 +1341,9 @@ namespace bpkg // Cast for compiling with ODB (see above). // assert (static_cast<bool> (out_root)); + + // Note that out_root is always relative. + // return configuration / *out_root; } @@ -1004,8 +1351,23 @@ 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("") + + #pragma db member(dependency_alternatives) id_column("package") \ + value_column("position") section(dependency_alternatives_section) + + #pragma db member(dependency_alternatives_section) load(lazy) update(always) + + #pragma db member(config_variables) id_column("package") value_column("") + + // For the sake of simplicity let's not calculate the checksum during + // migration. It seems that the only drawback of this approach is a + // (single) spurious reconfiguration of a dependency of a dependent with + // configuration clause previously configured by bpkg with the database + // schema version prior to 24. + // + #pragma db member(config_checksum) default("") // Explicit aggregate initialization for C++20 (private default ctor). // @@ -1021,6 +1383,7 @@ namespace bpkg optional<dir_path> sr, bool ps, optional<std::string> mc, + optional<std::string> bc, optional<dir_path> o, package_prerequisites pps) : name (move (n)), @@ -1035,6 +1398,7 @@ namespace bpkg src_root (move (sr)), purge_src (ps), manifest_checksum (move (mc)), + buildfiles_checksum (move (bc)), out_root (move (o)), prerequisites (move (pps)) {} @@ -1049,23 +1413,49 @@ namespace bpkg return os << p.string (); } + // Create a transient (or fake, if you prefer) available_package object + // corresponding to the specified selected object, which is expected to not + // be in the broken state. Note that the package locations list is left + // empty. + // + shared_ptr<available_package> + make_available (const common_options&, + database&, + const shared_ptr<selected_package>&); + + // 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<shared_ptr<selected_package>, 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. // + // Pass the build2 project info for the package, if available, to speed up + // the call and NULL otherwise (in which case it will be queried by the + // implementation). In the former case it is assumed that the package info + // has been retrieved with the b_info_flags::subprojects flag. + // // Notes: // // - The package directory is considered an iteration of the package if this // upstream version and revision is already present (selected) in the - // configuration and has a source directory. If that's the case, then the - // specified directory path and the checksum of the manifest file it - // contains are compared to the ones of the package present in the - // configuration. If both match, then the present package version - // (including its iteration, if any) is returned. Otherwise (the package - // has moved and/or the packaging information has changed), the present - // package version with the incremented iteration number is returned. Note - // that the directory path is matched only for the external selected - // packages. + // configuration and has a source directory. If that's the case and if the + // present version is not external (the package is being switched to a + // local potentially amended version), then the present package version + // with the incremented iteration number is returned. Otherwise (the + // present package is external), the specified directory path and the + // package checksum (see package_checksum() for details) are compared to + // the ones of the package present in the configuration. If both match, + // then the present package version (including its iteration, if any) is + // returned. Otherwise (the package has moved and/or the package + // information has changed), the present package version with the + // incremented iteration number is returned. // // - Only a single package iteration is valid per version in the // configuration. This, in particular, means that a package of the @@ -1079,15 +1469,16 @@ namespace bpkg // - The manifest file located in the specified directory is not parsed, and // so is not checked to match the specified package name and version. // - class common_options; - + // Note: loads selected packages. + // optional<version> package_iteration (const common_options&, - const dir_path& configuration, + database&, transaction&, const dir_path&, const package_name&, const version&, + const package_info*, bool check_external); // certificate @@ -1179,25 +1570,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<version_constraint> 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 +1595,125 @@ namespace bpkg optional<version_constraint> constraint; }; + // In the specified database query dependents of a dependency that resided + // in a potentially different database (yeah, it's a mouthful). + // + odb::result<package_dependent> + query_dependents (database& dependent_db, + const package_name& dependency, + database& dependency_db); + + // As above but cache the result in a vector. This version should be used if + // query_dependents*() may be called recursively. + // + vector<package_dependent> + query_dependents_cache (database&, const package_name&, database&); + + // 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 package_key + { + reference_wrapper<database> db; + package_name name; + + package_key (database& d, package_name n): db (d), name (move (n)) {} + + bool + operator== (const package_key& v) const + { + // See operator==(database, database). + // + return name == v.name && &db.get () == &v.db.get (); + } + + bool + operator!= (const package_key& v) const + { + return !(*this == v); + } + + bool + operator< (const package_key&) const; + + // Return the package string representation in the form: + // + // <name>[ <config-dir>] + // + std::string + string () const; + }; + + inline ostream& + operator<< (ostream& os, const package_key& p) + { + return os << p.string (); + } + + // Database, package name, and package version. + // + // It is normally used as a key for maps containing data for package + // versions across multiple linked configurations. Assumes that the + // respective databases are not detached during such map lifetimes. + // Considers all package name, package version, and database for objects + // comparison. + // + // The package name can be a pseudo-package (command line as a dependent, + // etc), in which case the version is absent. The version can also be empty, + // denoting a package of an unknown version. + // + struct package_version_key + { + reference_wrapper<database> db; + package_name name; + optional<bpkg::version> version; + + package_version_key (database& d, package_name n, bpkg::version v) + : db (d), name (move (n)), version (move (v)) {} + + // Create a pseudo-package (command line as a dependent, etc). + // + package_version_key (database& d, string n) + : db (d), + name (move (n), package_name::raw_string) {} + + bool + operator== (const package_version_key& v) const + { + // See operator==(database, database). + // + return name == v.name && + version == v.version && + &db.get () == &v.db.get (); + } + + bool + operator!= (const package_version_key& v) const + { + return !(*this == v); + } + + bool + operator< (const package_version_key&) const; + + // Return the package string representation in the form: + // + // <name>[/<version>] [ <config-dir>] + // + std::string + string (bool ignore_version = false) const; + }; + + inline ostream& + operator<< (ostream& os, const package_version_key& p) + { + return os << p.string (); + } + // Return a count of repositories that contain this repository fragment. // #pragma db view table("main.repository_fragments") @@ -1472,6 +1979,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 <typename T1, typename T2> inline auto compare_version_gt (const T1& x, const T2& y, bool revision, bool iteration) |