diff options
author | Karen Arutyunov <karen@codesynthesis.com> | 2021-06-22 22:04:52 +0300 |
---|---|---|
committer | Karen Arutyunov <karen@codesynthesis.com> | 2021-07-01 16:38:54 +0300 |
commit | 441cd214f04b8738c3fd92dd3c1a8ad1f2e56ba8 (patch) | |
tree | 49727c21918e0ef74a7c7e367b6396073d61f9aa | |
parent | ede8a15c2e310b2ae30894dd640f58d87a8a9408 (diff) |
Add support for building dependency in other configuration and re-pointing dependents to it
-rw-r--r-- | bpkg/database.hxx | 4 | ||||
-rw-r--r-- | bpkg/package.hxx | 3 | ||||
-rw-r--r-- | bpkg/pkg-build.cxx | 891 | ||||
-rw-r--r-- | bpkg/pkg-command.hxx | 2 | ||||
-rw-r--r-- | bpkg/pkg-configure.cxx | 28 | ||||
-rw-r--r-- | bpkg/pkg-configure.hxx | 25 | ||||
-rw-r--r-- | bpkg/types.hxx | 17 | ||||
-rw-r--r-- | tests/common/associated/t7a/libfax-1.0.0.tar.gz | bin | 0 -> 349 bytes | |||
-rw-r--r-- | tests/common/associated/t7a/libfix-1.0.0.tar.gz | bin | 373 -> 379 bytes | |||
-rw-r--r-- | tests/pkg-build.testscript | 979 |
10 files changed, 1722 insertions, 227 deletions
diff --git a/bpkg/database.hxx b/bpkg/database.hxx index 735fe7f..4e54bb7 100644 --- a/bpkg/database.hxx +++ b/bpkg/database.hxx @@ -352,8 +352,8 @@ namespace bpkg associated_databases implicit_associations_; }; - // NOTE: remember to update config_package comparison operators if changing - // the database comparison operators. + // NOTE: remember to update config_package comparison operators and + // compare_lazy_ptr if changing the database comparison operators. // // Note that here we use the database address as the database identity since // we don't suppose two database instances for the same configuration to diff --git a/bpkg/package.hxx b/bpkg/package.hxx index 3457073..4b0ab3a 100644 --- a/bpkg/package.hxx +++ b/bpkg/package.hxx @@ -418,7 +418,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; diff --git a/bpkg/pkg-build.cxx b/bpkg/pkg-build.cxx index c70aa9e..4c10067 100644 --- a/bpkg/pkg-build.cxx +++ b/bpkg/pkg-build.cxx @@ -250,6 +250,27 @@ namespace bpkg return r; } + // Compare two shared pointers via the pointed-to object addresses. + // + struct compare_shared_ptr + { + template <typename P> + bool + operator() (const P& x, const P& y) const + { + return x.get () < y.get (); + } + }; + + // The current configuration dependents being repointed to prerequisites in + // other configurations, together with their replacement flags. The flag is + // is true for the replacement prerequisites and false for the prerequisites + // being replaced. The unamended prerequisites have no flag associated. + // + using repointed_dependents = map<shared_ptr<selected_package>, + map<config_package, bool>, + compare_shared_ptr>; + // A "dependency-ordered" list of packages and their prerequisites. // That is, every package on the list only possibly depending on the // ones after it. In a nutshell, the usage is as follows: we first @@ -373,11 +394,19 @@ namespace bpkg // strings config_vars; - // Set of package names that caused this package to be built or adjusted. - // Empty name signifies user selection. + // Set of packages that caused this package to be built or adjusted. Empty + // name signifies user selection and can be present regardless of the + // required_by_dependents flag value. // set<config_package> required_by; + // Required-by packages have different semantics for different actions: + // the dependent for regular builds and dependency for adjustments and + // repointed dependent reconfiguration builds. Mixing them would break + // prompts/diagnostics. + // + bool required_by_dependents; + bool user_selection () const { @@ -417,6 +446,10 @@ namespace bpkg // static const uint16_t adjust_reconfigure = 0x0002; + // Set if we only need to configure this package. + // + static const uint16_t adjust_configure_only = 0x0004; + bool reconfigure () const { @@ -431,6 +464,12 @@ namespace bpkg (!system && !config_vars.empty ())))); } + bool + configure_only () const + { + return (adjustments & adjust_configure_only) != 0; + } + const version& available_version () const { @@ -505,12 +544,9 @@ namespace bpkg required_by.emplace (db.get ().main_database (), package_name ()); } - // Required-by package names have different semantics for different - // actions: dependent for builds and prerequisite for adjustment. Mixing - // them would break prompts/diagnostics, so we copy them only if actions - // match. + // Copy the required-by package names only if semantics matches. // - if (p.action && *p.action == *action) + if (p.required_by_dependents == required_by_dependents) required_by.insert (p.required_by.begin (), p.required_by.end ()); // Copy constraints. @@ -575,9 +611,23 @@ namespace bpkg // version was, in fact, added to the map and NULL if it was already there // or the existing version was preferred. So can be used as bool. // + // Also, in the recursive mode: + // + // - Use the custom search function to find the package dependency + // databases. + // + // - For the repointed dependents collect the prerequisite replacements + // rather than prerequisites being replaced. + // + // - Add configurations where the private host configuration were created + // into the specified database list. + // build_package* collect_build (const common_options& options, build_package pkg, + const function<find_prereq_database_function>& find_db, + const repointed_dependents& rpt_depts, + associated_databases& host_cfg_assocs, postponed_packages* recursively = nullptr) { using std::swap; // ...and not list::swap(). @@ -751,37 +801,75 @@ namespace bpkg // reasoning wrong. // if (recursively != nullptr) - collect_build_prerequisites (options, p, recursively); + collect_build_prerequisites (options, + p, + recursively, + find_db, + rpt_depts, + host_cfg_assocs); return &p; } - // Collect prerequisites of the package being built recursively. But first - // "prune" this process if the package we build is a system one or is - // already configured since that would mean all its prerequisites are - // configured as well. Note that this is not merely an optimization: the - // package could be an orphan in which case the below logic will fail (no - // repository fragment in which to search for prerequisites). By skipping - // the prerequisite check we are able to gracefully handle configured - // orphans. + // Collect prerequisites of the package being built recursively. + // + // But first "prune" this process if the package we build is a system one + // or is already configured and is not a repointed dependent, since that + // would mean all its prerequisites are configured as well. Note that this + // is not merely an optimization: the package could be an orphan in which + // case the below logic will fail (no repository fragment in which to + // search for prerequisites). By skipping the prerequisite check we are + // able to gracefully handle configured orphans. + // + // For the repointed dependent, we still need to collect its prerequisite + // replacements to make sure its constraints over them are satisfied. Note + // that, as it was said above, we can potentially fail if the dependent is + // an orphan, but this is exactly what we need to do in that case, since + // we won't be able to be reconfigure it anyway. // void - collect_build_prerequisites (const common_options& options, - const build_package& pkg, - postponed_packages* postponed) + collect_build_prerequisites ( + const common_options& options, + const build_package& pkg, + postponed_packages* postponed, + const function<find_prereq_database_function>& find_db, + const repointed_dependents& rpt_depts, + associated_databases& host_cfg_assocs) { tracer trace ("collect_build_prerequisites"); assert (pkg.action && *pkg.action == build_package::build); + if (pkg.system) + return; + const shared_ptr<selected_package>& sp (pkg.selected); - if (pkg.system || - (sp != nullptr && - sp->state == package_state::configured && - sp->substate != package_substate::system && - sp->version == pkg.available_version ())) - return; + // True if this is an up/down-grade. + // + bool ud (false); + + // If this is a repointed dependent, then it points to its prerequisite + // replacements flag map (see repointed_dependents for details). + // + const map<config_package, bool>* rpt_prereq_flags (nullptr); + + // Bail out if this is a configured non-system package and no + // up/down-grade nor collecting prerequisite replacements are required. + // + if (sp != nullptr && + sp->state == package_state::configured && + sp->substate != package_substate::system) + { + ud = sp->version != pkg.available_version (); + + repointed_dependents::const_iterator i (rpt_depts.find (sp)); + if (i != rpt_depts.end ()) + rpt_prereq_flags = &i->second; + + if (!ud && rpt_prereq_flags == nullptr) + return; + } // Show how we got here if things go wrong. // @@ -850,12 +938,12 @@ namespace bpkg // const version_constraint* dep_constr (nullptr); - // If the dependency package build is already in the map, then switch - // to its configuration. - // - database* ddb (&pdb); + database* ddb (find_db (pdb, dn, da.buildtime)); + + auto i (ddb != nullptr + ? map_.find (*ddb, dn) + : map_.find_dependency (pdb, dn, da.buildtime)); - auto i (map_.find_dependency (pdb, dn, da.buildtime)); if (i != map_.end ()) { const build_package& bp (i->second.package); @@ -888,8 +976,6 @@ namespace bpkg info << "specify " << dn << " version to satisfy " << name << " constraint"; } - - ddb = &bp.db.get (); } const dependency& d (!dep_constr @@ -901,10 +987,22 @@ namespace bpkg // constraint, then we don't want to be forcing its upgrade (or, // worse, downgrade). // - // Search recursively in the explicitly associated configurations. + // If the prerequisite configuration is explicitly specified by the + // user, then search for the prerequisite in this specific + // configuration. Otherwise, search recursively in the explicitly + // associated configurations of the dependent configuration. + // + // Note that for the repointed dependent we will always find the + // prerequisite replacement rather than the prerequisite being + // replaced. // pair<shared_ptr<selected_package>, database*> spd ( - find_dependency (*ddb, dn, da.buildtime)); + ddb != nullptr + ? make_pair (ddb->find<selected_package> (dn), ddb) + : find_dependency (pdb, dn, da.buildtime)); + + if (ddb == nullptr) + ddb = &pdb; shared_ptr<selected_package>& dsp (spd.first); @@ -917,20 +1015,32 @@ namespace bpkg if (dsp != nullptr) { - // Fail if we end up building a dependency that is also configured - // in another configuration of the same type. - // - if (i != map_.end () && *ddb != *spd.second) - fail << "building package " << dn << " which is already " - << dsp->state << " in another configuration" << - info << "building in " << ddb->config_orig << - info << dsp->state << " in " << spd.second->config_orig << - info << "use --config-* to select package configuration"; - // Switch to the selected package configuration. // ddb = spd.second; + // If we are collecting prerequisites of the repointed dependent, + // then only proceed further if this is either a replacement or + // unamended prerequisite and we are up/down-grading (only for the + // latter). + // + if (rpt_prereq_flags != nullptr) + { + auto i (rpt_prereq_flags->find (config_package {*ddb, dn})); + + bool unamended (i == rpt_prereq_flags->end ()); + bool replacement (!unamended && i->second); + + // We can never end up with the prerequisite being replaced, since + // the find_db() function should always return the replacement + // instead (see above). + // + assert (unamended || replacement); + + if (!(replacement || (unamended && ud))) + continue; + } + if (dsp->state == package_state::broken) fail << "unable to build broken package " << dn << *ddb << info << "use 'pkg-purge --force' to remove"; @@ -1048,6 +1158,11 @@ namespace bpkg nullopt /* name */, true /* sys_rep */)); + // Save the parent configuration of the newly-created private host + // configuration for the subsequent re-association. + // + host_cfg_assocs.push_back (*ddb); + hdb = &ddb->find_attached (*ac->id); } @@ -1201,6 +1316,7 @@ namespace bpkg false, // Checkout purge. strings (), // Configuration variables. {config_package {pdb, name}}, // Required by (dependent). + true, // Required by dependents. 0}; // Adjustments. // Add our constraint, if we have one. @@ -1232,7 +1348,12 @@ namespace bpkg // build foo ?sys:bar/2 // const build_package* p ( - collect_build (options, move (bp), postponed)); + collect_build (options, + move (bp), + find_db, + rpt_depts, + host_cfg_assocs, + postponed)); if (p != nullptr && force && !dep_optional) { @@ -1277,6 +1398,87 @@ namespace bpkg } } + // Collect the repointed dependents and their replaced prerequisites, + // recursively. + // + // If a repointed dependent is already pre-entered or collected with an + // action other than adjustment, then just mark it for reconfiguration + // unless it is already implied. Otherwise, collect the package build with + // the configure-only flag. + // + void + collect_repointed_dependents ( + const common_options& o, + database& mdb, + const repointed_dependents& rpt_depts, + build_packages::postponed_packages& postponed, + const function<find_prereq_database_function>& find_db, + associated_databases& host_cfg_assocs) + { + for (const auto& rd: rpt_depts) + { + const shared_ptr<selected_package>& sp (rd.first); + + auto i (map_.find (mdb, sp->name)); + if (i != map_.end ()) + { + build_package& b (i->second.package); + + if (!b.action || *b.action != build_package::adjust) + { + if (!b.action || + (*b.action != build_package::drop && !b.reconfigure ())) + b.adjustments |= build_package::adjust_reconfigure; + + continue; + } + } + + // The repointed dependent can be an orphan, so just create the + // available package from the selected package. + // + auto rp (make_available (o, mdb, sp)); + + // Add the prerequisite replacements as the required-by packages. + // + set<config_package> required_by; + for (const auto& prq: rd.second) + { + if (prq.second) // Prerequisite replacement? + { + const config_package& cp (prq.first); + required_by.emplace (cp.db, cp.name); + } + } + + build_package p { + build_package::build, + mdb, + sp, + move (rp.first), + move (rp.second), + nullopt, // Hold package. + nullopt, // Hold version. + {}, // Constraints. + sp->system (), + false, // Keep output directory. + nullopt, // Checkout root. + false, // Checkout purge. + strings (), // Configuration variables. + move (required_by), // Required by (dependencies). + false, // Required by dependents. + (build_package::adjust_reconfigure | + build_package::adjust_configure_only)}; + + collect_build (o, + move (p), + find_db, + rpt_depts, + host_cfg_assocs, + &postponed); + } + } + // Collect the package being dropped. // void @@ -1299,6 +1501,7 @@ namespace bpkg false, // Checkout purge. strings (), // Configuration variables. {}, // Required by. + false, // Required by dependents. 0}; // Adjustments. auto i (map_.find (db, nm)); @@ -1307,12 +1510,8 @@ namespace bpkg { build_package& bp (i->second.package); - // Can't think of the scenario when this happens. We would start - // collecting from scratch (see below). - // - assert (!bp.action || *bp.action != build_package::build); - - // Overwrite the existing (possibly pre-entered or adjustment) entry. + // Overwrite the existing (possibly pre-entered, adjustment, or + // repointed dependend build) entry. // bp = move (p); } @@ -1351,6 +1550,7 @@ namespace bpkg false, // Checkout purge. strings (), // Configuration variables. {}, // Required by. + false, // Required by dependents. build_package::adjust_unhold}; p.merge (move (bp)); @@ -1361,19 +1561,33 @@ namespace bpkg } void - collect_build_prerequisites (const common_options& o, - database& db, - const package_name& name, - postponed_packages& postponed) + collect_build_prerequisites ( + const common_options& o, + database& db, + const package_name& name, + postponed_packages& postponed, + const function<find_prereq_database_function>& find_db, + const repointed_dependents& rpt_depts, + associated_databases& host_cfg_assocs) { auto mi (map_.find (db, name)); assert (mi != map_.end ()); - collect_build_prerequisites (o, mi->second.package, &postponed); + + collect_build_prerequisites (o, + mi->second.package, + &postponed, + find_db, + rpt_depts, + host_cfg_assocs); } void - collect_build_postponed (const common_options& o, - postponed_packages& pkgs) + collect_build_postponed ( + const common_options& o, + postponed_packages& pkgs, + const function<find_prereq_database_function>& find_db, + const repointed_dependents& rpt_depts, + associated_databases& host_cfg_assocs) { // Try collecting postponed packages for as long as we are making // progress. @@ -1383,7 +1597,12 @@ namespace bpkg postponed_packages npkgs; for (const build_package* p: pkgs) - collect_build_prerequisites (o, *p, prog ? &npkgs : nullptr); + collect_build_prerequisites (o, + *p, + prog ? &npkgs : nullptr, + find_db, + rpt_depts, + host_cfg_assocs); assert (prog); // collect_build_prerequisites() should have failed. prog = (npkgs != pkgs); @@ -1395,8 +1614,9 @@ namespace bpkg // returning its positions. // // If buildtime is nullopt, then search for the specified package build in - // only the specified database. Otherwise, treat the package as a - // dependency and search for the build recursively (see + // only the specified configuration. Otherwise, treat the package as a + // dependency and use the custom search function to find its build + // configuration. Failed that, search for it recursively (see // config_package_map::find_dependency() for details). // // Recursively order the package dependencies being ordered failing if a @@ -1407,10 +1627,11 @@ namespace bpkg order (database& db, const package_name& name, optional<bool> buildtime, + const function<find_prereq_database_function>& find_db, bool reorder = true) { config_package_names chain; - return order (db, name, buildtime, chain, reorder); + return order (db, name, buildtime, chain, find_db, reorder); } // If a configured package is being up/down-graded then that means @@ -1429,7 +1650,7 @@ namespace bpkg // to also notice this. // void - collect_order_dependents () + collect_order_dependents (const repointed_dependents& rpt_depts) { // For each package on the list we want to insert all its dependents // before it so that they get configured after the package on which @@ -1450,12 +1671,13 @@ namespace bpkg // Dropped package may have no dependents. // if (*p.action != build_package::drop && p.reconfigure ()) - collect_order_dependents (i); + collect_order_dependents (i, rpt_depts); } } void - collect_order_dependents (iterator pos) + collect_order_dependents (iterator pos, + const repointed_dependents& rpt_depts) { tracer trace ("collect_order_dependents"); @@ -1482,24 +1704,46 @@ namespace bpkg package_name& dn (pd.name); auto i (map_.find (ddb, dn)); - // First make sure the up/downgraded package still satisfies this - // dependent. + // Make sure the up/downgraded package still satisfies this + // dependent. But first "prune" if this is a replaced prerequisite + // of the repointed dependent. + // + // Note that the repointed dependents are always collected and have + // all their collected prerequisites ordered (including new and old + // ones). See collect_build_prerequisites() and order() for details. // bool check (ud != 0 && pd.constraint); - // There is one tricky aspect: the dependent could be in the process - // of being up/downgraded as well. In this case all we need to do is - // detect this situation and skip the test since all the (new) - // contraints of this package have been satisfied in - // collect_build(). - // - if (check && i != map_.end () && i->second.position != end ()) + if (i != map_.end () && i->second.position != end ()) { build_package& dp (i->second.package); - check = dp.available == nullptr || - (dp.selected->system () == dp.system && - dp.selected->version == dp.available_version ()); + const shared_ptr<selected_package>& dsp (dp.selected); + + repointed_dependents::const_iterator j (rpt_depts.find (sp)); + + if (j != rpt_depts.end ()) + { + const map<config_package, bool>& prereqs_flags (j->second); + + auto k (prereqs_flags.find (config_package {pdb, n})); + + if (k != prereqs_flags.end () && !k->second) + continue; + } + + // There is one tricky aspect: the dependent could be in the + // process of being up/downgraded as well. In this case all we + // need to do is detect this situation and skip the test since all + // the (new) contraints of this package have been satisfied in + // collect_build(). + // + if (check) + { + check = dp.available == nullptr || + (dsp->system () == dp.system && + dsp->version == dp.available_version ()); + } } if (check) @@ -1569,6 +1813,7 @@ namespace bpkg false, // Checkout purge. strings (), // Configuration variables. {config_package {pdb, n}}, // Required by (dependency). + false, // Required by dependents. build_package::adjust_reconfigure}; }; @@ -1587,7 +1832,7 @@ namespace bpkg build_package& dp (i->second.package); iterator& dpos (i->second.position); - if (!dp.action || // Pre-entered. + if (!dp.action || // Pre-entered. *dp.action != build_package::build || // Non-build. dpos == end ()) // Build not in the list. { @@ -1640,7 +1885,7 @@ namespace bpkg // configured packages due to a dependency cycle (see order() for // details). // - collect_order_dependents (i->second.position); + collect_order_dependents (i->second.position, rpt_depts); } } } @@ -1680,14 +1925,24 @@ namespace bpkg const package_name& name, optional<bool> buildtime, config_package_names& chain, + const function<find_prereq_database_function>& find_db, bool reorder) { + config_package_map::iterator mi; + + if (buildtime) + { + database* ddb (find_db (db, name, *buildtime)); + + mi = ddb != nullptr + ? map_.find (*ddb, name) + : map_.find_dependency (db, name, *buildtime); + } + else + mi = map_.find (db, name); + // Every package that we order should have already been collected. // - auto mi (!buildtime - ? map_.find (db, name) - : map_.find_dependency (db, name, *buildtime)); - assert (mi != map_.end ()); build_package& p (mi->second.package); @@ -1786,6 +2041,10 @@ namespace bpkg // be disfigured during the plan execution, then we must order its // (current) dependencies that also need to be disfigured. // + // And yet, if the package we are ordering is a repointed dependent, + // then we must order not only its unamended and new prerequisites but + // also its replaced prerequisites, which can also be disfigured. + // bool src_conf (sp != nullptr && sp->state == package_state::configured && sp->substate != package_substate::system); @@ -1818,12 +2077,17 @@ namespace bpkg // The prerequisites may not necessarily be in the map. // + // Note that for the repointed dependent we also order its new and + // replaced prerequisites here, since they all are in the selected + // package prerequisites set. + // auto i (map_.find (db, name)); if (i != map_.end () && i->second.package.action) update (order (db, name, nullopt /* buildtime */, chain, + find_db, false /* reorder */)); } @@ -1850,10 +2114,15 @@ namespace bpkg if (da.buildtime && (dn == "build2" || dn == "bpkg")) continue; + // Note that for the repointed dependent we only order its new and + // unamended prerequisites here. Its replaced prerequisites will + // be ordered below. + // update (order (pdb, d.name, da.buildtime, chain, + find_db, false /* reorder */)); } } @@ -1872,11 +2141,17 @@ namespace bpkg // auto i (map_.find (db, name)); + // Note that for the repointed dependent we also order its replaced + // and potentially new prerequisites here (see above). The latter is + // redundant (we may have already ordered them above) but harmless, + // since we do not reorder. + // if (i != map_.end () && disfigure (i->second.package)) update (order (db, name, nullopt /* buildtime */, chain, + find_db, false /* reorder */)); } } @@ -2019,6 +2294,7 @@ namespace bpkg // struct evaluate_result { + reference_wrapper<database> db; shared_ptr<available_package> available; shared_ptr<bpkg::repository_fragment> repository_fragment; bool unused; @@ -2044,12 +2320,20 @@ namespace bpkg const shared_ptr<selected_package>&, const optional<version_constraint>& desired, bool desired_sys, + database& desired_db, bool patch, bool explicitly, const set<shared_ptr<repository_fragment>>&, const config_package_dependents&, bool ignore_unsatisfiable); + // If there are no user expectations regarding this dependency, then we give + // no up/down-grade recommendation, unless there are no dependents in which + // case we recommend to drop the dependency. + // + // Note that the user expectations are only applied for dependencies that + // have dependents in the current configuration. + // static optional<evaluate_result> evaluate_dependency (database& db, const shared_ptr<selected_package>& sp, @@ -2062,37 +2346,44 @@ namespace bpkg const package_name& nm (sp->name); - // If there are no user expectations regarding this dependency, then we - // give no up/down-grade recommendation, unless there are no dependents - // in which case we recommend to drop the dependency. + database& mdb (db.main_database ()); + + // Only search for the user expectations regarding this dependency if it + // has dependents in the current configuration. // - // Note that it would be easier to check for the dependent's presence - // first and, if present, for the user expectations afterwords. We, - // however, don't want to needlessly query all the explicitly associated - // databases (which can be many) for dependents if we can bail out - // earlier. + auto mdb_deps (query_dependents (mdb, nm, db)); // Stash not re-query. + bool mdb_dep (!mdb_deps.empty ()); + + auto i (mdb_dep + ? find_if (deps.begin (), deps.end (), + [&nm] (const dependency_package& i) + { + return i.name == nm; + }) + : deps.end ()); + + bool user_exp (i != deps.end () && i->db.type == db.type); + bool copy_dep (user_exp && i->db != db); + + // If the dependency needs to be copied, then only consider it dependents + // in the current configuration for the version constraints, etc. // - auto i (find_if ( - deps.begin (), deps.end (), - [&nm, &db] (const dependency_package& i) - { - return i.name == nm && i.db == db; - })); - - bool no_rec (i == deps.end ()); + associated_databases dbs (copy_dep + ? associated_databases ({mdb}) + : db.dependent_configs ()); vector<pair<database&, package_dependent>> pds; - for (database& ddb: db.dependent_configs ()) + for (database& ddb: dbs) { - auto ds (query_dependents (ddb, nm, db)); + auto ds (ddb.main () ? move (mdb_deps) : query_dependents (ddb, nm, db)); // Bail out if the dependency is used but there are no user expectations // regrading it. // if (!ds.empty ()) { - if (no_rec) + if (!user_exp) return nullopt; for (auto& d: ds) @@ -2106,7 +2397,8 @@ namespace bpkg { l5 ([&]{trace << *sp << db << ": unused";}); - return evaluate_result {nullptr /* available */, + return evaluate_result {db, + nullptr /* available */, nullptr /* repository_fragment */, true /* unused */, false /* system */}; @@ -2122,14 +2414,17 @@ namespace bpkg // const optional<version_constraint>& dvc (i->constraint); // May be nullopt. bool dsys (i->system); + database& ddb (i->db); - if (ssys == dsys && - dvc && - (ssys ? sv == *dvc->min_version : satisfies (sv, dvc))) + if (ssys == dsys && + dvc && + (ssys ? sv == *dvc->min_version : satisfies (sv, dvc)) && + db == ddb) { l5 ([&]{trace << *sp << db << ": unchanged";}); - return evaluate_result {nullptr /* available */, + return evaluate_result {db, + nullptr /* available */, nullptr /* repository_fragment */, false /* unused */, false /* system */}; @@ -2142,8 +2437,6 @@ namespace bpkg set<shared_ptr<repository_fragment>> repo_frags; config_package_dependents dependents; - database& mdb (db.main_database ()); - for (auto& pd: pds) { database& ddb (pd.first); @@ -2171,6 +2464,7 @@ namespace bpkg sp, dvc, dsys, + ddb, i->patch, true /* explicitly */, repo_frags, @@ -2206,6 +2500,7 @@ namespace bpkg const shared_ptr<selected_package>& sp, const optional<version_constraint>& dvc, bool dsys, + database& ddb, bool patch, bool explicitly, const set<shared_ptr<repository_fragment>>& rfs, @@ -2217,9 +2512,10 @@ namespace bpkg const package_name& nm (sp->name); const version& sv (sp->version); - auto no_change = [] () + auto no_change = [&db] () { - return evaluate_result {nullptr /* available */, + return evaluate_result {db, + nullptr /* available */, nullptr /* repository_fragment */, false /* unused */, false /* system */}; @@ -2285,7 +2581,7 @@ namespace bpkg // // Note that we also handle a package stub here. // - if (!dvc && av < sv) + if (!dvc && av < sv && db == ddb) { assert (!dsys); // Version can't be empty for the system package. @@ -2349,24 +2645,24 @@ namespace bpkg // match the ones of the selected package, then no package change is // required. Otherwise, recommend an up/down-grade. // - if (av == sv && ssys == dsys) + if (av == sv && ssys == dsys && db == ddb) { l5 ([&]{trace << *sp << db << ": unchanged";}); return no_change (); } l5 ([&]{trace << *sp << db << ": update to " - << package_string (nm, av, dsys);}); + << package_string (nm, av, dsys) << ddb;}); return evaluate_result { - move (ap), move (af.second), false /* unused */, dsys}; + ddb, move (ap), move (af.second), false /* unused */, dsys}; } // If we aim to upgrade to the latest version, then what we currently have // is the only thing that we can get, and so returning the "no change" // result, unless we need to upgrade a package configured as system. // - if (!dvc && !ssys) + if (!dvc && !ssys && db == ddb) { assert (!dsys); // Version cannot be empty for the system package. @@ -2381,7 +2677,7 @@ namespace bpkg // if (ignore_unsatisfiable) { - l5 ([&]{trace << package_string (nm, dvc, dsys) << db + l5 ([&]{trace << package_string (nm, dvc, dsys) << ddb << (unsatisfiable.empty () ? ": no source" : ": unsatisfiable");}); @@ -2409,7 +2705,7 @@ namespace bpkg << "from its dependents' repositories"; } else if (!stub) - fail << package_string (nm, dsys ? nullopt : dvc) << db + fail << package_string (nm, dsys ? nullopt : dvc) << ddb << " is not available from its dependents' repositories"; else // The only available package is a stub. { @@ -2418,7 +2714,7 @@ namespace bpkg // assert (!dvc && !dsys && ssys); - fail << package_string (nm, dvc) << db << " is not available in " + fail << package_string (nm, dvc) << ddb << " is not available in " << "source from its dependents' repositories"; } } @@ -2426,7 +2722,7 @@ namespace bpkg // Issue the diagnostics and fail. // diag_record dr (fail); - dr << "package " << nm << db << " doesn't satisfy its dependents"; + dr << "package " << nm << ddb << " doesn't satisfy its dependents"; // Print the list of unsatisfiable versions together with dependents they // don't satisfy: up to three latest versions with no more than five @@ -2606,6 +2902,7 @@ namespace bpkg sp, nullopt /* desired */, false /*desired_sys */, + db, !*upgrade /* patch */, false /* explicitly */, repo_frags, @@ -2621,7 +2918,10 @@ namespace bpkg // Return false if the plan execution was noop. // static bool - execute_plan (const pkg_build_options&, build_package_list&, bool simulate); + execute_plan (const pkg_build_options&, + build_package_list&, + bool simulate, + const function<find_prereq_database_function>&); using pkg_options = pkg_build_pkg_options; @@ -3027,7 +3327,7 @@ namespace bpkg t.commit (); - // Fetch the repositories in the main configuration. + // Fetch the repositories in the current configuration. // // Note that during this build only the repositories information from // the main database will be used. @@ -3546,6 +3846,10 @@ namespace bpkg imaginary_stubs = move (stubs); } + // List of packages specified on the command line. + // + vector<config_package> conf_pkgs; + // Separate the packages specified on the command line into to hold and to // up/down-grade as dependencies, and save dependents whose dependencies // must be upgraded recursively. @@ -3558,14 +3862,14 @@ namespace bpkg // Check if the package is a duplicate. Return true if it is but // harmless. // - map<config_package, pkg_arg> package_map; + map<package_name, pkg_arg> package_map; auto check_dup = [&package_map, &arg_string, &arg_parsed] (const pkg_arg& pa) -> bool { assert (arg_parsed (pa)); - auto r (package_map.emplace (config_package {pa.db, pa.name}, pa)); + auto r (package_map.emplace (pa.name, pa)); const pkg_arg& a (r.first->second); assert (arg_parsed (a)); @@ -3580,6 +3884,7 @@ namespace bpkg if (!r.second && (a.scheme != pa.scheme || a.name != pa.name || + a.db != pa.db || a.constraint != pa.constraint || !compare_options (a.options, pa.options) || a.config_vars != pa.config_vars)) @@ -3914,6 +4219,8 @@ namespace bpkg // sp = pdb.find<selected_package> (pa.name); + conf_pkgs.emplace_back (pdb, pa.name); + dep_pkgs.push_back ( dependency_package {pdb, move (pa.name), @@ -4072,8 +4379,8 @@ namespace bpkg assert (sp != nullptr && sp->system () == arg_sys (pa)); auto rp (make_available (o, pdb, sp)); - ap = rp.first; - af = rp.second; // Could be NULL (orphan). + ap = move (rp.first); + af = move (rp.second); // Could be NULL (orphan). } // We will keep the output directory only if the external package is @@ -4104,6 +4411,7 @@ namespace bpkg pa.options.checkout_purge (), move (pa.config_vars), {config_package {mdb, ""}}, // Required by (command line). + false, // Required by dependents. 0}; // Adjustments. l4 ([&]{trace << "stashing held package " @@ -4118,6 +4426,8 @@ namespace bpkg p.constraints.emplace_back ( mdb, "command line", move (*pa.constraint)); + conf_pkgs.emplace_back (p.db, p.name ()); + hold_pkgs.push_back (move (p)); } @@ -4197,6 +4507,7 @@ namespace bpkg false, // Checkout purge. strings (), // Configuration variables. {config_package {mdb, ""}}, // Required by (command line). + false, // Required by dependents. 0}; // Adjustments. l4 ([&]{trace << "stashing held package " @@ -4224,6 +4535,40 @@ namespace bpkg return 0; } + // Search for the package prerequisite among packages specified on the + // command line and, if found, return its desired database. Return NULL + // otherwise. The `db` argument specifies the dependent database. + // + // Note that the semantics of a package specified on the command line is: + // build the package in the specified configuration (current by default) + // and repoint all dependents in the current configuration of this + // prerequisite to this new prerequisite. Thus, the function always + // returns NULL for dependents not in the current configuration. + // + // Also note that we rely on "small function object" optimization here. + // + const function<find_prereq_database_function> find_prereq_database ( + [&conf_pkgs] (database& db, + const package_name& nm, + bool buildtime) -> database* + { + if (db.main ()) + { + auto i (find_if (conf_pkgs.begin (), conf_pkgs.end (), + [&nm] (const config_package& i) + { + return i.name == nm; + })); + + const char* tp (buildtime ? "host" : db.type.c_str ()); + + if (i != conf_pkgs.end () && i->db.type == tp) + return &i->db; + } + + return nullptr; + }); + // Assemble the list of packages we will need to build-to-hold, still used // dependencies to up/down-grade, and unused dependencies to drop. We call // this the plan. @@ -4284,6 +4629,58 @@ namespace bpkg }; vector<dep> deps; + // Map the repointed dependents to the replacement flags (see + // repointed_dependents for details). + // + // Note that the overall plan is to add the replacement prerequisites to + // the repointed dependents prerequisites sets at the beginning of the + // refinement loop iteration and remove them right before the plan + // execution simulation. This will allow the collecting/ordering + // functions to see both kinds of prerequisites (being replaced and + // their replacements) and only consider one kind or another or both, as + // appropriate. + // + repointed_dependents rpt_depts; + { + transaction t (mdb); + + using query = query<selected_package>; + + query q (query::state == "configured"); + + for (shared_ptr<selected_package> sp: + pointer_result (mdb.query<selected_package> (q))) + { + map<config_package, bool> ps; // Old/new prerequisites. + + for (const auto& p: sp->prerequisites) + { + database& db (p.first.database ()); + const package_name& name (p.first.object_id ()); + + auto i (find_if (conf_pkgs.begin (), conf_pkgs.end (), + [&name] (const config_package& i) + { + return i.name == name; + })); + + // Only consider a prerequisite if its new configuration is of the + // same type as an old one. + // + if (i != conf_pkgs.end () && i->db != db && i->db.type == db.type) + { + ps.emplace (config_package {i->db, name}, true); + ps.emplace (config_package { db, name}, false); + } + } + + if (!ps.empty ()) + rpt_depts.emplace (move (sp), move (ps)); + } + + t.commit (); + } + // Iteratively refine the plan with dependency up/down-grades/drops. // for (bool refine (true), scratch (true); refine; ) @@ -4293,13 +4690,55 @@ namespace bpkg transaction t (mdb); - // Save the total number of host configurations in the associated - // configurations cluster to later check if any private host - // configurations have been created during collection of the package - // builds (see below). + // Temporarily add the replacement prerequisites to the repointed + // dependent prerequisites sets and persist the changes. + // + // Note that we don't copy the prerequisite constraints into the + // replacements, since they are unused in the collecting/ordering + // logic. // - size_t host_configs ( - mdb.dependency_configs (true /* buildtime */).size ()); + for (auto& rd: rpt_depts) + { + const shared_ptr<selected_package>& sp (rd.first); + + for (const auto& prq: rd.second) + { + if (prq.second) // Prerequisite replacement? + { + const config_package& cp (prq.first); + + auto i (sp->prerequisites.emplace ( + lazy_shared_ptr<selected_package> (cp.db, cp.name), + nullopt)); + + // The selected package should only contain the old + // prerequisites at this time, so adding a replacement should + // always succeed. + // + assert (i.second); + } + } + + mdb.update (sp); + } + + // Private host configurations that were created during collecting the + // package builds. + // + // Note that the private host configurations are associated to their + // parent configurations right after being created, so that the + // subsequent collecting, ordering, and plan execution simulation + // logic can use them. However, we can not easily commit these changes + // at some point, since there could also be some other changes made to + // the database which needs to be rolled back at the end of the + // refinement iteration. + // + // This, the plan is to collect configurations where the private host + // configurations were created and, after the transaction is rolled + // back, re-associate these configurations and persist the changes + // using the new transaction. + // + associated_databases host_cfg_assocs; build_packages::postponed_packages postponed; @@ -4334,6 +4773,7 @@ namespace bpkg p.checkout_purge, p.config_vars, {config_package {mdb, ""}}, // Required by (command line). + false, // Required by dependents. 0}; // Adjustments. if (p.constraint) @@ -4348,12 +4788,22 @@ namespace bpkg // specify packages on the command line does not matter). // for (const build_package& p: hold_pkgs) - pkgs.collect_build (o, p); + pkgs.collect_build (o, + p, + find_prereq_database, + rpt_depts, + host_cfg_assocs); // Collect all the prerequisites of the user selection. // for (const build_package& p: hold_pkgs) - pkgs.collect_build_prerequisites (o, p.db, p.name (), postponed); + pkgs.collect_build_prerequisites (o, + p.db, + p.name (), + postponed, + find_prereq_database, + rpt_depts, + host_cfg_assocs); // Note that we need to collect unheld after prerequisites, not to // overwrite the pre-entered entries before they are used to provide @@ -4365,6 +4815,16 @@ namespace bpkg pkgs.collect_unhold (p.db, p.selected); } + // Collect dependents which dependencies needs to be repointed to + // prerequisites from different configurations. + // + pkgs.collect_repointed_dependents (o, + mdb, + rpt_depts, + postponed, + find_prereq_database, + host_cfg_assocs); + scratch = false; } else @@ -4412,28 +4872,26 @@ namespace bpkg false, // Checkout purge. strings (), // Configuration variables. {config_package {mdb, ""}}, // Required by (command line). + false, // Required by dependents. 0}; // Adjustments. - pkgs.collect_build (o, move (p), &postponed /* recursively */); + pkgs.collect_build (o, + move (p), + find_prereq_database, + rpt_depts, + host_cfg_assocs, + &postponed /* recursively */); } } // Handle the (combined) postponed collection. // if (!postponed.empty ()) - pkgs.collect_build_postponed (o, postponed); - - // If any private host configurations have been created while - // collecting the package builds, then commit the new associations and - // restart the transaction (there should be no changes other than - // that). - // - if (mdb.dependency_configs (true /* buildtime */).size () != - host_configs) - { - t.commit (); - t.start (mdb); - } + pkgs.collect_build_postponed (o, + postponed, + find_prereq_database, + rpt_depts, + host_cfg_assocs); // Now that we have collected all the package versions that we need to // build, arrange them in the "dependency order", that is, with every @@ -4451,17 +4909,28 @@ namespace bpkg for (const dep& d: deps) pkgs.order (d.db, d.name, - nullopt /* buildtime */, - false /* reorder */); + nullopt /* buildtime */, + find_prereq_database, + false /* reorder */); for (const build_package& p: reverse_iterate (hold_pkgs)) - pkgs.order (p.db, p.name (), nullopt /* buildtime */); + pkgs.order (p.db, + p.name (), + nullopt /* buildtime */, + find_prereq_database); + + for (const auto& rd: rpt_depts) + pkgs.order (mdb, + rd.first->name, + nullopt /* buildtime */, + find_prereq_database, + false /* reorder */); // Collect and order all the dependents that we will need to // reconfigure because of the up/down-grades of packages that are now // on the list. // - pkgs.collect_order_dependents (); + pkgs.collect_order_dependents (rpt_depts); // And, finally, make sure all the packages that we need to unhold // are on the list. @@ -4471,8 +4940,37 @@ namespace bpkg if (p.selected != nullptr && p.selected->hold_package) pkgs.order (p.db, p.name, - nullopt /* buildtime */, - false /* reorder */); + nullopt /* buildtime */, + find_prereq_database, + false /* reorder */); + } + + // Now, as we done with package builds collecting/ordering, erase the + // replacements from the repointed dependents prerequisite sets and + // persist the changes. + // + for (auto& rd: rpt_depts) + { + const shared_ptr<selected_package>& sp (rd.first); + + for (const auto& prq: rd.second) + { + if (prq.second) // Prerequisite replacement? + { + const config_package& cp (prq.first); + + size_t n (sp->prerequisites.erase ( + lazy_shared_ptr<selected_package> (cp.db, cp.name))); + + // The selected package should always contain the prerequisite + // replacement at this time, so its removal should always + // succeed. + // + assert (n == 1); + } + } + + mdb.update (sp); } // We are about to execute the plan on the database (but not on the @@ -4512,7 +5010,10 @@ namespace bpkg vector<build_package> tmp (pkgs.begin (), pkgs.end ()); build_package_list bl (tmp.begin (), tmp.end ()); - changed = execute_plan (o, bl, true /* simulate */); + changed = execute_plan (o, + bl, + true /* simulate */, + find_prereq_database); if (changed) { @@ -4533,7 +5034,7 @@ namespace bpkg // value covers both the "no change is required" and the "no // recommendation available" cases. // - auto eval_dep = [&dep_pkgs, &rec_pkgs] ( + auto eval_dep = [&dep_pkgs, &rec_pkgs] ( database& db, const shared_ptr<selected_package>& sp, bool ignore_unsatisfiable = true) -> optional<evaluate_result> @@ -4672,7 +5173,7 @@ namespace bpkg continue; if (!diag) - deps.push_back (dep {adb, + deps.push_back (dep {er->db, sp->name, move (er->available), move (er->repository_fragment), @@ -4714,7 +5215,7 @@ namespace bpkg // also verified but not included into the resulting set. // using prerequisites = set<lazy_shared_ptr<selected_package>, - compare_lazy_ptr>; + compare_lazy_ptr_id>; map<config_package, prerequisites> cache; small_vector<config_selected_package, 16> chain; @@ -4760,8 +5261,8 @@ namespace bpkg chain.push_back (csp); - // Verify all prerequisites, but only collect those that are from - // configurations of the same type. + // Verify all prerequisites, but only collect those corresponding + // to the runtime dependencies. // // Indeed, we don't care if an associated host configuration // contains a configured package that we also have configured in @@ -4770,6 +5271,18 @@ namespace bpkg // the same package (of potentially different versions) configured // in different host configurations. // + // Note, however, that we cannot easily determine if the + // prerequisites corresponds to the runtime or + // build-time dependency, since we only store the version + // constraint for it. The current implementation relies on the + // fact that the build-time dependency configuration type (host) + // differs from the dependent configuration type (target as a + // common case) and doesn't work well for the self-hosted + // configurations. For them it can fail erroneously. We can + // potentially fix that by additionally storing the build-time + // flag besides the version constraint. However, let's first see + // if it ever becomes a problem. + // prerequisites r; const package_prerequisites& prereqs (sp->prerequisites); @@ -4792,7 +5305,7 @@ namespace bpkg // for (const lazy_shared_ptr<selected_package>& p: ps) { - // Note: compare_lazy_ptr only considers package names. + // Note: compare_id_lazy_ptr only considers package names. // auto i (r.find (p)); @@ -4973,6 +5486,18 @@ namespace bpkg } } + // Re-associate the private host configurations that were created + // during collecting the package builds with their parent + // configurations. Note that these associations were lost on the + // previous transaction rollback. + // + for (database& db: host_cfg_assocs) + cfg_add (db, + db.config / host_dir, + true /* relative */, + nullopt /* name */, + true /* sys_rep */); + t.commit (); } } @@ -5065,13 +5590,15 @@ namespace bpkg // if (!p.reconfigure () && sp->state == package_state::configured && - (!p.user_selection () || o.configure_only ())) + (!p.user_selection () || + o.configure_only () || + p.configure_only ())) continue; act = p.system ? "reconfigure" : (p.reconfigure () - ? (o.configure_only () + ? (o.configure_only () || p.configure_only () ? "reconfigure" : "reconfigure/update") : "update"); @@ -5091,7 +5618,10 @@ namespace bpkg act += "/unhold"; act += ' ' + p.available_name_version_db (); - cause = "required by"; + cause = p.required_by_dependents ? "required by" : "dependent of"; + + if (p.configure_only ()) + update_dependents = true; } string rb; @@ -5178,7 +5708,7 @@ namespace bpkg // prerequsites got upgraded/downgraded and that the user may want to in // addition update (that update_dependents flag above). // - execute_plan (o, pkgs, false /* simulate */); + execute_plan (o, pkgs, false /* simulate */, find_prereq_database); if (o.configure_only ()) return 0; @@ -5196,7 +5726,7 @@ namespace bpkg { assert (p.action); - if (*p.action != build_package::build) + if (*p.action != build_package::build || p.configure_only ()) continue; database& db (p.db); @@ -5222,7 +5752,14 @@ namespace bpkg database& db (p.db); - if (*p.action == build_package::adjust && p.reconfigure ()) + // Here we distinguish the repointed dependent reconfiguration build + // by the configure_only flag presence. If we ever add support for the + // package-specific --configure-only option, we will need to changed + // that (invent an additional "repointed dependent" flag or similar). + // + if ((*p.action == build_package::adjust || + (*p.action == build_package::build && p.configure_only ())) && + p.reconfigure ()) upkgs.push_back (pkg_command_vars {db.config_orig, db.main (), p.selected, @@ -5245,7 +5782,8 @@ namespace bpkg static bool execute_plan (const pkg_build_options& o, build_package_list& build_pkgs, - bool simulate) + bool simulate, + const function<find_prereq_database_function>& find_db) { tracer trace ("execute_plan"); @@ -5629,6 +6167,17 @@ namespace bpkg transaction t (pdb, !simulate /* start */); + // Show how we got here if things go wrong, for example selecting a + // prerequisite is ambiguous due to the dependency package being + // configured in multiple associated configurations. + // + auto g ( + make_exception_guard ( + [&p] () + { + info << "while configuring " << p.name () << p.db; + })); + // Note that pkg_configure() commits the transaction. // if (p.system) @@ -5643,7 +6192,8 @@ namespace bpkg sp, ap->dependencies, p.config_vars, - simulate); + simulate, + find_db); else // Dependent. { // Must be in the unpacked state since it was disfigured on the first @@ -5662,7 +6212,8 @@ namespace bpkg sp, convert (move (m.dependencies)), p.config_vars, - simulate); + simulate, + find_db); } r = true; diff --git a/bpkg/pkg-command.hxx b/bpkg/pkg-command.hxx index 9feba0f..0ed7072 100644 --- a/bpkg/pkg-command.hxx +++ b/bpkg/pkg-command.hxx @@ -52,7 +52,7 @@ namespace bpkg bool cwd; // Change the working directory to the package directory. // Return the selected package name/version followed by the configuration - // directory, unless this is the main configuration. For example: + // directory, unless this is the current configuration. For example: // // libfoo/1.1.0 // libfoo/1.1.0 [cfg/] diff --git a/bpkg/pkg-configure.cxx b/bpkg/pkg-configure.cxx index 5c26fca..8161fbb 100644 --- a/bpkg/pkg-configure.cxx +++ b/bpkg/pkg-configure.cxx @@ -19,11 +19,13 @@ using namespace butl; namespace bpkg { package_prerequisites - pkg_configure_prerequisites (const common_options& o, - database& db, - transaction&, - const dependencies& deps, - const package_name& package) + pkg_configure_prerequisites ( + const common_options& o, + database& db, + transaction&, + const dependencies& deps, + const package_name& package, + const function<find_prereq_database_function>& find_db) { package_prerequisites r; @@ -58,8 +60,12 @@ namespace bpkg } } + database* ddb (find_db ? find_db (db, n, da.buildtime) : nullptr); + pair<shared_ptr<selected_package>, database*> spd ( - find_dependency (db, n, da.buildtime)); + ddb != nullptr + ? make_pair (ddb->find<selected_package> (n), ddb) + : find_dependency (db, n, da.buildtime)); if (const shared_ptr<selected_package>& dp = spd.first) { @@ -115,7 +121,8 @@ namespace bpkg const shared_ptr<selected_package>& p, const dependencies& deps, const strings& vars, - bool simulate) + bool simulate, + const function<find_prereq_database_function>& find_db) { tracer trace ("pkg_configure"); @@ -142,7 +149,12 @@ namespace bpkg // assert (p->prerequisites.empty ()); - p->prerequisites = pkg_configure_prerequisites (o, db, t, deps, p->name); + p->prerequisites = pkg_configure_prerequisites (o, + db, + t, + deps, + p->name, + find_db); if (!simulate) { diff --git a/bpkg/pkg-configure.hxx b/bpkg/pkg-configure.hxx index e9e64b6..2e2a495 100644 --- a/bpkg/pkg-configure.hxx +++ b/bpkg/pkg-configure.hxx @@ -20,6 +20,16 @@ namespace bpkg int pkg_configure (const pkg_configure_options&, cli::scanner& args); + // The custom search function, if specified, is called by pkg_configure() + // and pkg_configure_prerequisites() to obtain the database to search for + // the prerequisite in, instead of searching for it in the associated + // databases, recursively. If the function returns NULL, then fallback to + // the recursive search through the associated databases. + // + using find_prereq_database_function = database* (database&, + const package_name&, + bool buildtime); + // Note: all of the following functions expect the package dependency // constraints to be complete. @@ -32,7 +42,8 @@ namespace bpkg const shared_ptr<selected_package>&, const dependencies&, const strings& config_vars, - bool simulate); + bool simulate, + const function<find_prereq_database_function>& = {}); // Configure a system package and commit the transaction. // @@ -50,11 +61,13 @@ namespace bpkg // Note: loads selected packages. // package_prerequisites - pkg_configure_prerequisites (const common_options&, - database&, - transaction&, - const dependencies&, - const package_name&); + pkg_configure_prerequisites ( + const common_options&, + database&, + transaction&, + const dependencies&, + const package_name&, + const function<find_prereq_database_function>& = {}); } #endif // BPKG_PKG_CONFIGURE_HXX diff --git a/bpkg/types.hxx b/bpkg/types.hxx index 7d8740f..1c6e89b 100644 --- a/bpkg/types.hxx +++ b/bpkg/types.hxx @@ -165,9 +165,24 @@ namespace bpkg } }; + struct compare_lazy_ptr + { + template <typename P> + bool + operator() (const P& x, const P& y) const + { + // See operator==(database, database). + // + return x.object_id () != y.object_id () + ? (x.object_id () < y.object_id ()) + : (&static_cast<typename P::base_type> (x).database () < + &static_cast<typename P::base_type> (y).database ()); + } + }; + // Compare two lazy pointers via the pointed-to object ids. // - struct compare_lazy_ptr + struct compare_lazy_ptr_id { template <typename P> bool diff --git a/tests/common/associated/t7a/libfax-1.0.0.tar.gz b/tests/common/associated/t7a/libfax-1.0.0.tar.gz Binary files differnew file mode 100644 index 0000000..a460e4a --- /dev/null +++ b/tests/common/associated/t7a/libfax-1.0.0.tar.gz diff --git a/tests/common/associated/t7a/libfix-1.0.0.tar.gz b/tests/common/associated/t7a/libfix-1.0.0.tar.gz Binary files differindex 471a75f..fbfadde 100644 --- a/tests/common/associated/t7a/libfix-1.0.0.tar.gz +++ b/tests/common/associated/t7a/libfix-1.0.0.tar.gz diff --git a/tests/pkg-build.testscript b/tests/pkg-build.testscript index e06df77..2dbe9b2 100644 --- a/tests/pkg-build.testscript +++ b/tests/pkg-build.testscript @@ -810,10 +810,10 @@ test.options += --no-progress $cfg_add -d cfg cfg2; $* libbaz ?libbar +{ --config-id 1 } libfoo/1.0.0 +{ --config-id 1 } 2>>~%EOE% != 0 error: unable to satisfy constraints on package libfoo - % info: libbar \[cfg2.\] depends on \(libfoo == 1.1.0\)% info: command line depends on (libfoo == 1.0.0) - info: available libfoo/1.1.0 + % info: libbar \[cfg2.\] depends on \(libfoo == 1.1.0\)% info: available libfoo/1.0.0 + info: available libfoo/1.1.0 info: explicitly specify libfoo version to manually satisfy both constraints %info: while satisfying libbar/1.1.0 \[cfg2.\]% info: while satisfying libbaz/1.1.0 @@ -2463,18 +2463,21 @@ test.options += --no-progress $cfg_create -d cfg2 &cfg2/***; $cfg_add -d cfg cfg2; - $* libbar/0.0.1 +{ --config-id 1 } 2>!; + $* libbar/0.0.1 2>!; - $pkg_status -d cfg2 libbaz >'libbaz configured 0.0.1'; + $pkg_status libbaz >'libbaz configured 0.0.1 available 0.1.0 0.0.4 0.0.3'; - $* ?libbaz/0.0.3 +{ --config-id 1 } 2>>~%EOE% != 0; - %error: package libbaz \[cfg2.\] doesn't satisfy its dependents% - % info: libbaz/0.0.3 doesn't satisfy libbar/0.0.1 \[cfg2.\]% + $* ?libbaz/0.0.3 +{ --config-id 1 } 2>>EOE != 0; + error: unable to satisfy constraints on package libbaz + info: libbar depends on (libbaz == 0.0.1) + info: command line depends on (libbaz == 0.0.3) + info: specify libbaz version to satisfy libbar constraint + info: while satisfying libbar/0.0.1 EOE - $pkg_status -d cfg2 libbaz >'libbaz configured 0.0.1'; + $pkg_status libbaz >'libbaz configured 0.0.1 available 0.1.0 0.0.4 0.0.3'; - $pkg_drop -d cfg2 libbar + $pkg_drop libbar } } @@ -4269,7 +4272,7 @@ else libfoo [cfg-bar-foo/] configured 1.1.0 EOO - $pkg_status -d cfg-bar-foo -r libbar >>EOO; + $pkg_status -d cfg-bar-foo -r libbar >>/EOO; libbar configured 1.1.0 libfoo configured 1.1.0 EOO @@ -4278,7 +4281,7 @@ else $pkg_drop libbaz; - $pkg_status libbaz libbar libfoo >>EOO + $pkg_status libbaz libbar libfoo >>/EOO libbaz available 1.1.0 libbar available [1.1.0] libfoo available [1.1.0] 1.0.0 @@ -4361,7 +4364,7 @@ else %purged libbar/1.1.0 \[cfg-bar.\]% EOE - $pkg_status libbar libfoo >>EOO + $pkg_status libbar libfoo >>/EOO libbar available [1.1.0] libfoo available [1.1.0] 1.0.0 EOO @@ -4377,10 +4380,10 @@ else : { +$clone_cfg - +$cfg_create -d cfg2 --type host --name alt-host &cfg2/*** + +$cfg_create -d cfg2 --type host --name cfg2 &cfg2/*** +$cfg_add -d cfg cfg2 - : upgrade-dependency + : downgrade-dependency : { $clone_cfg; @@ -4451,41 +4454,28 @@ else libbaz configured 1.0.0 available 1.1.0 EOO - # Now upgrade libbaz in cfg/ and downgrade it in cfg2/. Note that - # libbaz/1.1.0 doesn't exist in the t7a repository where libbar/1.0.0 - # belongs to, thus we build it to hold rather than a dependency. - # - # While at it, test the --config-name option. - # - $* libbar/1.0.0 libbaz/1.1.0 ?foo/1.0.0 +{ --config-name alt-host } \ + $* libbar/1.0.0 ?foo/1.0.0 +{ --config-name cfg2 } \ ?libbaz/1.0.0 +{ --config-id 1 } <'y' 2>>~%EOE%; % downgrade libbaz/1.0.0 \[cfg2.\]% % downgrade foo/1.0.0 \[cfg2.\]% - upgrade libbaz/1.1.0 downgrade libbar/1.0.0 continue? [Y/n] disfigured libbar/1.1.0 - disfigured libbaz/1.0.0 %disfigured foo/1.1.0 \[cfg2.\]% %disfigured libbaz/1.1.0 \[cfg2.\]% %fetched libbaz/1.0.0 \[cfg2.\]% %unpacked libbaz/1.0.0 \[cfg2.\]% %fetched foo/1.0.0 \[cfg2.\]% %unpacked foo/1.0.0 \[cfg2.\]% - fetched libbaz/1.1.0 - unpacked libbaz/1.1.0 fetched libbar/1.0.0 unpacked libbar/1.0.0 %configured libbaz/1.0.0 \[cfg2.\]% %configured foo/1.0.0 \[cfg2.\]% - configured libbaz/1.1.0 configured libbar/1.0.0 - %info: .+libbaz-1.0.0.+ is up to date% - %info: .+foo-1.0.0.+ is up to date% - %info: .+libbaz-1.1.0.+ is up to date% - %info: .+libbar-1.0.0.+ is up to date% + %info: cfg2.+libbaz-1.0.0.+ is up to date% + %info: cfg2.+foo-1.0.0.+ is up to date% + %info: cfg.+libbar-1.0.0.+ is up to date% %updated libbaz/1.0.0 \[cfg2.\]% %updated foo/1.0.0 \[cfg2.\]% - updated libbaz/1.1.0 updated libbar/1.0.0 EOE @@ -4493,7 +4483,7 @@ else !libbar configured !1.0.0 available 1.1.0 foo [cfg2/] configured !1.0.0 available 1.1.0 libbaz [cfg2/] configured !1.0.0 available 1.1.0 - !libbaz configured !1.1.0 + libbaz configured 1.0.0 available 1.1.0 EOO $pkg_drop libbar libbaz @@ -4518,12 +4508,12 @@ else $* libbar ?foo +{ --config-id 2 } --yes 2>!; - $pkg_status -r libbar >>/EOE; + $pkg_status -r libbar >>/EOO; !libbar configured 1.0.0 foo [cfg3/] configured 1.0.0 libbaz [cfg3/] configured 1.0.0 libbaz configured 1.0.0 - EOE + EOO $pkg_drop libbar } @@ -4555,12 +4545,12 @@ else updated libbar/1.0.0 EOE - $pkg_status -r libbar >>/EOE; + $pkg_status -r libbar >>/EOO; !libbar configured 1.0.0 foo [cfg2/] configured 1.0.0 !libbaz [cfg3/] configured 1.0.0 libbaz configured 1.0.0 - EOE + EOO $pkg_drop libbar } @@ -4654,7 +4644,7 @@ else updated libbar/1.0.0 EOE - $pkg_status -r libbar >>EOO; + $pkg_status -r libbar >>/EOO; !libbar configured 1.0.0 foo configured 1.0.0 libbaz configured 1.0.0 @@ -4683,7 +4673,7 @@ else updated libbar/1.1.0 EOE - $pkg_status -r libbar >>EOO; + $pkg_status -r libbar >>/EOO; !libbar configured 1.1.0 foo configured 1.1.0 libbaz configured 1.1.0 @@ -4739,8 +4729,11 @@ else $cfg_add -d cfg cfg3; $* libfix --yes 2>>~%EOE%; + fetched libfax/1.0.0 + unpacked libfax/1.0.0 fetched libfix/1.0.0 unpacked libfix/1.0.0 + configured libfax/1.0.0 configured libfix/1.0.0 %info: .+libfix-1.0.0.+ is up to date% updated libfix/1.0.0 @@ -4749,4 +4742,914 @@ else $pkg_drop libfix } } + + : change-config + : + { + : copy + : + { + $cfg_create -d t1 --name t1 &t1/***; + $cfg_create -d t2 --name t2 &t2/***; + + $cfg_add -d t1 t2; + + $rep_add -d t1 $rep/t7a && $rep_fetch -d t1; + + $* -d t1 libbaz --yes 2>!; + + $pkg_status -d t1 -r >>/EOO; + !libbaz configured 1.0.0 + EOO + + $* -d t1 libbaz +{ --config-name t2 } 2>>~%EOE%; + %fetched libbaz/1.0.0 \[t2.\]% + %unpacked libbaz/1.0.0 \[t2.\]% + %configured libbaz/1.0.0 \[t2.\]% + %info: t2.+libbaz-1.0.0.+ is up to date% + %updated libbaz/1.0.0 \[t2.\]% + EOE + + $pkg_status -d t1 -r >>/EOO + !libbaz configured 1.0.0 + !libbaz [t2/] configured 1.0.0 + EOO + } + + : copy-unhold + : + { + $cfg_create -d t1 --name t1 &t1/***; + $cfg_create -d t2 --name t2 &t2/***; + + $cfg_add -d t1 t2; + + $rep_add -d t1 $rep/t7a && $rep_fetch -d t1; + + $* -d t1 libbaz --yes 2>!; + + $pkg_status -d t1 -r >>/EOO; + !libbaz configured 1.0.0 + EOO + + $* -d t1 ?libbaz +{ --config-name t2 }; + + $pkg_status -d t1 -r >>/EOO + !libbaz configured 1.0.0 + EOO + } + + : copy-point + : + { + $cfg_create -d t1 --name t1 &t1/***; + $cfg_create -d t2 --name t2 &t2/***; + + $cfg_add -d t1 t2; + + $rep_add -d t1 $rep/t7a && $rep_fetch -d t1; + + $* -d t1 libbaz --yes 2>!; + + $pkg_status -d t1 -r >>/EOO; + !libbaz configured 1.0.0 + EOO + + $* -d t1 foo libbaz +{ --config-name t2 } 2>>~%EOE%; + %fetched libbaz/1.0.0 \[t2.\]% + %unpacked libbaz/1.0.0 \[t2.\]% + fetched foo/1.0.0 + unpacked foo/1.0.0 + %configured libbaz/1.0.0 \[t2.\]% + configured foo/1.0.0 + %info: t2.+libbaz-1.0.0.+ is up to date% + %info: t1.+foo-1.0.0.+ is up to date% + %updated libbaz/1.0.0 \[t2.\]% + updated foo/1.0.0 + EOE + + $pkg_status -d t1 -r >>/EOO + !libbaz configured 1.0.0 + !foo configured 1.0.0 + !libbaz [t2/] configured 1.0.0 + !libbaz [t2/] configured 1.0.0 + EOO + } + + : copy-unhold-point + : + { + $cfg_create -d t1 --name t1 &t1/***; + $cfg_create -d t2 --name t2 &t2/***; + + $cfg_add -d t1 t2; + + $rep_add -d t1 $rep/t7a && $rep_fetch -d t1; + + $* -d t1 libbaz --yes 2>!; + + $pkg_status -d t1 -r >>/EOO; + !libbaz configured 1.0.0 + EOO + + $* -d t1 foo ?libbaz +{ --config-name t2 } 2>>~%EOE%; + %fetched libbaz/1.0.0 \[t2.\]% + %unpacked libbaz/1.0.0 \[t2.\]% + fetched foo/1.0.0 + unpacked foo/1.0.0 + %configured libbaz/1.0.0 \[t2.\]% + configured foo/1.0.0 + %info: t2.+libbaz-1.0.0.+ is up to date% + %info: t1.+foo-1.0.0.+ is up to date% + %updated libbaz/1.0.0 \[t2.\]% + updated foo/1.0.0 + EOE + + $pkg_status -d t1 -r >>/EOO + !libbaz configured 1.0.0 + !foo configured 1.0.0 + libbaz [t2/] configured 1.0.0 + EOO + } + + : copy-repoint + : + { + $cfg_create -d t1 --name t1 &t1/***; + $cfg_create -d t2 --name t2 &t2/***; + + $cfg_create -d h1 --type host --name h1 &h1/***; + $cfg_create -d h2 --type host --name h2 &h2/***; + + $cfg_add -d t1 h1; + $cfg_add -d t1 h2; + + $cfg_add -d t2 h1; + + $rep_add -d t1 $rep/t7a && $rep_fetch -d t1; + $rep_add -d t2 $rep/t7a && $rep_fetch -d t2; + + $* -d t1 libbar ?foo +{ --config-name h1 } <<EOI 2>>~%EOE%; + y + EOI + % new libbaz/1.0.0 \[h1.\] \(required by foo \[h1.\]\)% + % new foo/1.0.0 \[h1.\]% + new libbaz/1.0.0 (required by libbar) + new libbar/1.0.0 + %continue\? \[Y/n\] fetched libbaz/1.0.0 \[h1.\]% + %unpacked libbaz/1.0.0 \[h1.\]% + %fetched foo/1.0.0 \[h1.\]% + %unpacked foo/1.0.0 \[h1.\]% + fetched libbaz/1.0.0 + unpacked libbaz/1.0.0 + fetched libbar/1.0.0 + unpacked libbar/1.0.0 + %configured libbaz/1.0.0 \[h1.\]% + %configured foo/1.0.0 \[h1.\]% + configured libbaz/1.0.0 + configured libbar/1.0.0 + %info: h1.+foo-1.0.0.+ is up to date% + %info: t1.+libbar-1.0.0.+ is up to date% + %updated foo/1.0.0 \[h1.\]% + updated libbar/1.0.0 + EOE + + $pkg_status -d t1 -r >>/EOO; + !libbar configured 1.0.0 + foo [h1/] configured 1.0.0 + libbaz [h1/] configured 1.0.0 + libbaz configured 1.0.0 + EOO + + $* -d t2 libbox ?foo +{ --config-name h1 } <<EOI 2>>~%EOE%; + y + EOI + % update foo/1.0.0 \[h1.\]% + new libbaz/1.0.0 (required by libbox) + new libbox/1.0.0 + continue? [Y/n] fetched libbaz/1.0.0 + unpacked libbaz/1.0.0 + fetched libbox/1.0.0 + unpacked libbox/1.0.0 + configured libbaz/1.0.0 + configured libbox/1.0.0 + %info: h1.+foo-1.0.0.+ is up to date% + %info: t2.+libbox-1.0.0.+ is up to date% + %updated foo/1.0.0 \[h1.\]% + updated libbox/1.0.0 + EOE + + $pkg_status -d t2 -r >>/EOO; + !libbox configured 1.0.0 + foo [h1/] configured 1.0.0 + libbaz [h1/] configured 1.0.0 + libbaz configured 1.0.0 + EOO + + $* -d t1 ?foo +{ --config-name h2 } <<EOI 2>>~%EOE%; + y + y + EOI + % new libbaz/1.0.0 \[h2.\] \(required by foo \[h2.\]\)% + % new foo/1.0.0 \[h2.\]% + % reconfigure libbar/1.0.0 \(dependent of foo \[h2.\]\)% + continue? [Y/n] update dependent packages? [Y/n] disfigured libbar/1.0.0 + %fetched libbaz/1.0.0 \[h2.\]% + %unpacked libbaz/1.0.0 \[h2.\]% + %fetched foo/1.0.0 \[h2.\]% + %unpacked foo/1.0.0 \[h2.\]% + %configured libbaz/1.0.0 \[h2.\]% + %configured foo/1.0.0 \[h2.\]% + configured libbar/1.0.0 + %info: h2.+foo-1.0.0.+ is up to date% + %info: t1.+libbar-1.0.0.+ is up to date% + %updated foo/1.0.0 \[h2.\]% + updated libbar/1.0.0 + EOE + + $pkg_status -d t1 -r >>/EOO; + !libbar configured 1.0.0 + foo [h2/] configured 1.0.0 + libbaz [h2/] configured 1.0.0 + libbaz configured 1.0.0 + EOO + + $pkg_status -d t2 -r >>/EOO + !libbox configured 1.0.0 + foo [h1/] configured 1.0.0 + libbaz [h1/] configured 1.0.0 + libbaz configured 1.0.0 + EOO + } + + : copy-repoint-drop + : + { + $cfg_create -d t1 --name t1 &t1/***; + $cfg_create -d t2 --name t2 &t2/***; + + $cfg_add -d t1 t2; + + $rep_add -d t1 $rep/t7a && $rep_fetch -d t1; + + $* -d t1 foo --yes 2>!; + + $pkg_status -d t1 -r >>/EOO; + !foo configured 1.0.0 + libbaz configured 1.0.0 + EOO + + $* -d t1 libbaz +{ --config-name t2 } <<EOI 2>>~%EOE%; + y + y + EOI + % new libbaz/1.0.0 \[t2.\]% + drop libbaz/1.0.0 (unused) + % reconfigure foo/1.0.0 \(dependent of libbaz \[t2.\]\)% + continue? [Y/n] update dependent packages? [Y/n] disfigured foo/1.0.0 + disfigured libbaz/1.0.0 + %fetched libbaz/1.0.0 \[t2.\]% + %unpacked libbaz/1.0.0 \[t2.\]% + purged libbaz/1.0.0 + %configured libbaz/1.0.0 \[t2.\]% + configured foo/1.0.0 + %info: t2.+libbaz-1.0.0.+ is up to date% + %info: t1.+foo-1.0.0.+ is up to date% + %updated libbaz/1.0.0 \[t2.\]% + updated foo/1.0.0 + EOE + + $pkg_status -d t1 -r >>/EOO + !foo configured 1.0.0 + !libbaz [t2/] configured 1.0.0 + !libbaz [t2/] configured 1.0.0 + EOO + } + + : drop-repointed + : + { + $cfg_create -d t1 --name t1 &t1/***; + $cfg_create -d t2 --name t2 &t2/***; + + $cfg_add -d t1 t2; + + $rep_add -d t1 $rep/t7a && $rep_fetch -d t1; + + $* -d t1 foo --yes 2>!; + + $pkg_status -d t1 -r >>/EOO; + !foo configured 1.0.0 + libbaz configured 1.0.0 + EOO + + $* -d t1 ?foo libbaz +{ --config-name t2 } <<EOI 2>>~%EOE%; + y + EOI + % new libbaz/1.0.0 \[t2.\]% + drop libbaz/1.0.0 (unused) + drop foo/1.0.0 (unused) + continue? [Y/n] disfigured foo/1.0.0 + disfigured libbaz/1.0.0 + %fetched libbaz/1.0.0 \[t2.\]% + %unpacked libbaz/1.0.0 \[t2.\]% + purged libbaz/1.0.0 + purged foo/1.0.0 + %configured libbaz/1.0.0 \[t2.\]% + %info: t2.+libbaz-1.0.0.+ is up to date% + %updated libbaz/1.0.0 \[t2.\]% + EOE + + $pkg_status -d t1 -r >>/EOO + !libbaz [t2/] configured 1.0.0 + EOO + } + + : dependency-repointed + : + { + $cfg_create -d h1 --type host --name h1 &h1/***; + $cfg_create -d h2 --type host --name h2 &h2/***; + + $cfg_add -d h1 h2; + + $rep_add -d h1 $rep/t7a && $rep_fetch -d h1; + + $* -d h1 foo --yes 2>!; + + $pkg_status -d h1 -r >>/EOO; + !foo configured 1.0.0 + libbaz configured 1.0.0 + EOO + + $* -d h1 libbar libbaz +{ --config-name h2 } <<EOI 2>>~%EOE%; + y + EOI + % new libbaz/1.0.0 \[h2.\]% + drop libbaz/1.0.0 (unused) + reconfigure/update foo/1.0.0 (required by libbar) + new libbar/1.0.0 + continue? [Y/n] disfigured foo/1.0.0 + disfigured libbaz/1.0.0 + %fetched libbaz/1.0.0 \[h2.\]% + %unpacked libbaz/1.0.0 \[h2.\]% + purged libbaz/1.0.0 + fetched libbar/1.0.0 + unpacked libbar/1.0.0 + %configured libbaz/1.0.0 \[h2.\]% + configured foo/1.0.0 + configured libbar/1.0.0 + %info: h2.+libbaz-1.0.0.+ is up to date% + %info: h1.+libbar-1.0.0.+ is up to date% + %updated libbaz/1.0.0 \[h2.\]% + updated libbar/1.0.0 + EOE + + $pkg_status -d h1 -r >>/EOO + !foo configured 1.0.0 + !libbaz [h2/] configured 1.0.0 + !libbar configured 1.0.0 + !foo configured 1.0.0 + !libbaz [h2/] configured 1.0.0 + !libbaz [h2/] configured 1.0.0 + !libbaz [h2/] configured 1.0.0 + EOO + } + + : dependency-repointed-system + : + { + $cfg_create -d h1 --type host --name h1 &h1/***; + $cfg_create -d h2 --type host --name h2 &h2/***; + + $cfg_add -d h1 h2; + + $rep_add -d h1 $rep/t7a && $rep_fetch -d h1; + + $* -d h1 foo --yes 2>!; + + $pkg_status -d h1 -r >>/EOO; + !foo configured 1.0.0 + libbaz configured 1.0.0 + EOO + + $* -d h1 libbar '?sys:foo/1.2.0' ?libbaz +{ --config-name h2 } <<EOI 2>>~%EOE%; + y + EOI + % new libbaz/1.0.0 \[h2.\]% + drop libbaz/1.0.0 (unused) + reconfigure/unhold sys:foo/1.2.0 + new libbar/1.0.0 + continue? [Y/n] disfigured foo/1.0.0 + disfigured libbaz/1.0.0 + %fetched libbaz/1.0.0 \[h2.\]% + %unpacked libbaz/1.0.0 \[h2.\]% + purged libbaz/1.0.0 + purged foo/1.0.0 + fetched libbar/1.0.0 + unpacked libbar/1.0.0 + %configured libbaz/1.0.0 \[h2.\]% + configured sys:foo/1.2.0 + configured libbar/1.0.0 + %info: h2.+libbaz-1.0.0.+ is up to date% + %info: h1.+libbar-1.0.0.+ is up to date% + %updated libbaz/1.0.0 \[h2.\]% + updated libbar/1.0.0 + EOE + + $pkg_status -d h1 -r >>/EOO; + !libbar configured 1.0.0 + foo configured,system !1.2.0 + libbaz [h2/] configured 1.0.0 + EOO + + $* -d h1 ?foo ?libbaz <<EOI 2>>~%EOE%; + y + y + EOI + % drop libbaz/1.0.0 \[h2.\] \(unused\)% + new libbaz/1.0.0 + downgrade foo/1.0.0 + reconfigure libbar/1.0.0 (dependent of libbaz) + continue? [Y/n] update dependent packages? [Y/n] disfigured libbar/1.0.0 + purged foo/1.2.0 + %disfigured libbaz/1.0.0 \[h2.\]% + %purged libbaz/1.0.0 \[h2.\]% + fetched libbaz/1.0.0 + unpacked libbaz/1.0.0 + fetched foo/1.0.0 + unpacked foo/1.0.0 + configured libbaz/1.0.0 + configured foo/1.0.0 + configured libbar/1.0.0 + %info: h1.+libbaz-1.0.0.+ is up to date% + %info: h1.+foo-1.0.0.+ is up to date% + %info: h1.+libbar-1.0.0.+ is up to date% + updated libbaz/1.0.0 + updated foo/1.0.0 + updated libbar/1.0.0 + EOE + + $pkg_status -d h1 -r >>/EOO + !libbar configured 1.0.0 + foo configured 1.0.0 + libbaz configured 1.0.0 + libbaz configured 1.0.0 + EOO + } + + : orphan-repointed + : + { + $cfg_create -d h1 --type host --name h1 &h1/***; + $cfg_create -d h2 --type host --name h2 &h2/***; + + $cfg_add -d h1 h2; + + $rep_add -d h1 $rep/t7b && $rep_fetch -d h1; + + $* -d h1 foo --yes 2>!; + + $rep_remove -d h1 $rep/t7b; + $rep_add -d h1 $rep/t7a && $rep_fetch -d h1; + + $* -d h1 libbaz +{ --config-name h2 } 2>>EOE != 0 + error: package foo/1.1.0 is orphaned + info: explicitly upgrade it to a new version + info: while satisfying foo/1.1.0 + EOE + } + + : unhold-repointed + : + { + $cfg_create -d h1 --type host --name h1 &h1/***; + $cfg_create -d h2 --type host --name h2 &h2/***; + + $cfg_add -d h1 h2; + + $rep_add -d h1 $rep/t7a && $rep_fetch -d h1; + + $* -d h1 foo --yes 2>!; + + $pkg_status -d h1 -r >>/EOO; + !foo configured 1.0.0 + libbaz configured 1.0.0 + EOO + + $* -d h1 libbar ?foo libbaz +{ --config-name h2 } <<EOI 2>>~%EOE%; + y + EOI + % new libbaz/1.0.0 \[h2.\]% + drop libbaz/1.0.0 (unused) + reconfigure/update/unhold foo/1.0.0 + new libbar/1.0.0 + continue? [Y/n] disfigured foo/1.0.0 + disfigured libbaz/1.0.0 + %fetched libbaz/1.0.0 \[h2.\]% + %unpacked libbaz/1.0.0 \[h2.\]% + purged libbaz/1.0.0 + fetched libbar/1.0.0 + unpacked libbar/1.0.0 + %configured libbaz/1.0.0 \[h2.\]% + configured foo/1.0.0 + configured libbar/1.0.0 + %info: h2.+libbaz-1.0.0.+ is up to date% + %info: h1.+foo-1.0.0.+ is up to date% + %info: h1.+libbar-1.0.0.+ is up to date% + %updated libbaz/1.0.0 \[h2.\]% + updated foo/1.0.0 + updated libbar/1.0.0 + EOE + + $pkg_status -d h1 -r >>/EOO + !libbar configured 1.0.0 + foo configured 1.0.0 + !libbaz [h2/] configured 1.0.0 + !libbaz [h2/] configured 1.0.0 + !libbaz [h2/] configured 1.0.0 + EOO + } + + : satisfy + : + { + $cfg_create -d h1 --type host --name h1 &h1/***; + $cfg_create -d h2 --type host --name h2 &h2/***; + + $cfg_add -d h1 h2; + + $rep_add -d h2 $rep/t7b && $rep_fetch -d h2; + $* -d h2 foo --yes 2>!; + + $rep_add -d h1 $rep/t7a && $rep_fetch -d h1; + + $* -d h1 foo ?libbaz 2>>~%EOE%; + fetched libbaz/1.0.0 + unpacked libbaz/1.0.0 + fetched foo/1.0.0 + unpacked foo/1.0.0 + configured libbaz/1.0.0 + configured foo/1.0.0 + %info: h1.+libbaz-1.0.0.+ is up to date% + %info: h1.+foo-1.0.0.+ is up to date% + updated libbaz/1.0.0 + updated foo/1.0.0 + EOE + + $pkg_status -d h1 -r >>/EOO; + !foo configured 1.0.0 + libbaz configured 1.0.0 + !foo [h2/] configured 1.1.0 + libbaz [h2/] configured 1.1.0 + EOO + + $* -d h1 ?libbaz/1.0.0 +{ --config-name h2 } 2>>~%EOE% != 0; + %error: unable to downgrade package libbaz/1.1.0 \[h2.\] to 1.0.0% + % info: because package foo \[h2.\] depends on \(libbaz \^1.1.0\)% + info: explicitly request up/downgrade of package foo + info: or explicitly specify package libbaz version to manually satisfy these constraints + EOE + + $* -d h1 ?libbaz +{ --config-name h2 } <<EOI 2>>~%EOE%; + y + n + EOI + % update libbaz/1.1.0 \[h2.\]% + drop libbaz/1.0.0 (unused) + % reconfigure foo/1.0.0 \(dependent of libbaz \[h2.\]\)% + continue? [Y/n] update dependent packages? [Y/n] disfigured foo/1.0.0 + disfigured libbaz/1.0.0 + purged libbaz/1.0.0 + configured foo/1.0.0 + %info: h2.+libbaz-1.1.0.+ is up to date% + %updated libbaz/1.1.0 \[h2.\]% + EOE + + $pkg_status -d h1 -r >>/EOO + !foo configured 1.0.0 + libbaz [h2/] configured 1.1.0 + !foo [h2/] configured 1.1.0 + libbaz [h2/] configured 1.1.0 + EOO + } + + : upgrade-repointed + : + { + $cfg_create -d t1 --name t1 &t1/***; + + $rep_add -d t1 $rep/t7a && $rep_fetch -d t1; + + $* -d t1 libbar --yes 2>!; + + $pkg_status -d t1 -r >>/EOO; + !libbar configured 1.0.0 + foo [t1/.bpkg/host/] configured 1.0.0 + libbaz [t1/.bpkg/host/] configured 1.0.0 + libbaz configured 1.0.0 + EOO + + $cfg_create -d h1 --type host --name h1 &h1/***; + + $cfg_add -d t1 h1 2>!; + + $rep_add -d t1 $rep/t7b && $rep_fetch -d t1; + + $* -d t1 libbar ?foo +{ --config-name h1 } <<EOI 2>>~%EOE%; + y + EOI + % new libbaz/1.1.0 \[h1.\] \(required by foo \[h1.\]\)% + % new foo/1.1.0 \[h1.\]% + % drop libbaz/1.0.0 \[t1..bpkg.host.\] \(unused\)% + % drop foo/1.0.0 \[t1..bpkg.host.\] \(unused\)% + upgrade libbar/1.1.0 + continue? [Y/n] disfigured libbar/1.0.0 + %disfigured foo/1.0.0 \[t1..bpkg.host.\]% + %disfigured libbaz/1.0.0 \[t1..bpkg.host.\]% + %fetched libbaz/1.1.0 \[h1.\]% + %unpacked libbaz/1.1.0 \[h1.\]% + %fetched foo/1.1.0 \[h1.\]% + %unpacked foo/1.1.0 \[h1.\]% + %purged libbaz/1.0.0 \[t1..bpkg.host.\]% + %purged foo/1.0.0 \[t1..bpkg.host.\]% + fetched libbar/1.1.0 + unpacked libbar/1.1.0 + %configured libbaz/1.1.0 \[h1.\]% + %configured foo/1.1.0 \[h1.\]% + configured libbar/1.1.0 + %info: h1.+foo-1.1.0.+ is up to date% + %info: t1.+libbar-1.1.0.+ is up to date% + %updated foo/1.1.0 \[h1.\]% + updated libbar/1.1.0 + EOE + + $pkg_status -d t1 -r >>/EOO + !libbar configured 1.1.0 + foo [h1/] configured 1.1.0 + libbaz [h1/] configured 1.1.0 + libbaz configured 1.0.0 available 1.1.0 + EOO + } + + : upgrade-repointed-dependency + : + { + $cfg_create -d h1 --type host --name h1 &h1/***; + $cfg_create -d h2 --type host --name h2 &h2/***; + + $cfg_add -d h1 h2; + + $rep_add -d h1 $rep/t7a && $rep_fetch -d h1; + + $* -d h1 libbar --yes 2>!; + + $pkg_status -d h1 -r >>/EOO; + !libbar configured 1.0.0 + foo configured 1.0.0 + libbaz configured 1.0.0 + libbaz configured 1.0.0 + EOO + + $rep_add -d h1 $rep/t7b && $rep_fetch -d h1; + + $* -d h1 libbar ?foo ?libbaz +{ --config-name h2 } <<EOI 2>>~%EOE%; + y + y + EOI + % new libbaz/1.1.0 \[h2.\]% + drop libbaz/1.0.0 (unused) + upgrade foo/1.1.0 + upgrade libbar/1.1.0 + continue? [Y/n] disfigured libbar/1.0.0 + disfigured foo/1.0.0 + disfigured libbaz/1.0.0 + %fetched libbaz/1.1.0 \[h2.\]% + %unpacked libbaz/1.1.0 \[h2.\]% + purged libbaz/1.0.0 + fetched foo/1.1.0 + unpacked foo/1.1.0 + fetched libbar/1.1.0 + unpacked libbar/1.1.0 + %configured libbaz/1.1.0 \[h2.\]% + configured foo/1.1.0 + configured libbar/1.1.0 + %info: h2.+libbaz-1.1.0.+ is up to date% + %info: h1.+foo-1.1.0.+ is up to date% + %info: h1.+libbar-1.1.0.+ is up to date% + %updated libbaz/1.1.0 \[h2.\]% + updated foo/1.1.0 + updated libbar/1.1.0 + EOE + + $pkg_status -d h1 -r >>/EOO + !libbar configured 1.1.0 + foo configured 1.1.0 + libbaz [h2/] configured 1.1.0 + libbaz [h2/] configured 1.1.0 + EOO + } + + : upgrade-prerequisite-replacement + : + { + $cfg_create -d t1 --name t1 &t1/***; + + $rep_add -d t1 $rep/t7a && $rep_fetch -d t1; + + $* -d t1 libbar --yes 2>!; + + $pkg_status -d t1 -r >>/EOO; + !libbar configured 1.0.0 + foo [t1/.bpkg/host/] configured 1.0.0 + libbaz [t1/.bpkg/host/] configured 1.0.0 + libbaz configured 1.0.0 + EOO + + $cfg_create -d t2 --name t2 &t2/***; + + $rep_add -d t2 $rep/t7a && $rep_fetch -d t2; + + $* -d t2 libbaz --yes 2>!; + + $cfg_add -d t1 t2 2>!; + + $rep_add -d t1 $rep/t7b && $rep_fetch -d t1; + + $* -d t1 libbaz +{ --config-name t2 } <<EOI 2>>~%EOE%; + y + y + EOI + % upgrade libbaz/1.1.0 \[t2.\]% + drop libbaz/1.0.0 (unused) + % reconfigure libbar/1.0.0 \(dependent of libbaz \[t2.\]\)% + continue? [Y/n] update dependent packages? [Y/n] disfigured libbar/1.0.0 + disfigured libbaz/1.0.0 + %disfigured libbaz/1.0.0 \[t2.\]% + %fetched libbaz/1.1.0 \[t2.\]% + %unpacked libbaz/1.1.0 \[t2.\]% + purged libbaz/1.0.0 + %configured libbaz/1.1.0 \[t2.\]% + configured libbar/1.0.0 + %info: t2.+libbaz-1.1.0.+ is up to date% + %info: t1.+libbar-1.0.0.+ is up to date% + %updated libbaz/1.1.0 \[t2.\]% + updated libbar/1.0.0 + EOE + + $pkg_status -d t1 -r >>/EOO + !libbar configured 1.0.0 available 1.1.0 + foo [t1/.bpkg/host/] configured 1.0.0 available 1.1.0 + libbaz [t1/.bpkg/host/] configured 1.0.0 available 1.1.0 + !libbaz [t2/] configured 1.1.0 + !libbaz [t2/] configured 1.1.0 + EOO + } + + : copy-upgrade-dependency-tree + : + { + $cfg_create -d t1 --name t1 &t1/***; + + $rep_add -d t1 $rep/t7a && $rep_fetch -d t1; + + $* -d t1 libbar --yes 2>!; + + $pkg_status -d t1 -r >>/EOO; + !libbar configured 1.0.0 + foo [t1/.bpkg/host/] configured 1.0.0 + libbaz [t1/.bpkg/host/] configured 1.0.0 + libbaz configured 1.0.0 + EOO + + $cfg_create -d t2 --name t2 &t2/***; + + $cfg_add -d t1 t2 2>!; + + $rep_add -d t1 $rep/t7b && $rep_fetch -d t1; + + $* -d t1 libbar +{ --config-name t2 } <<EOI 2>>~%EOE%; + y + EOI + % new libbaz/1.1.0 \[t2..bpkg.host.\] \(required by foo \[t2..bpkg.host.\]\)% + % new foo/1.1.0 \[t2..bpkg.host.\] \(required by libbar \[t2.\]\)% + % new libbaz/1.1.0 \[t2.\] \(required by libbar \[t2.\]\)% + % new libbar/1.1.0 \[t2.\]% + %continue\? \[Y/n\] fetched libbaz/1.1.0 \[t2..bpkg.host.\]% + %unpacked libbaz/1.1.0 \[t2..bpkg.host.\]% + %fetched foo/1.1.0 \[t2..bpkg.host.\]% + %unpacked foo/1.1.0 \[t2..bpkg.host.\]% + %fetched libbaz/1.1.0 \[t2.\]% + %unpacked libbaz/1.1.0 \[t2.\]% + %fetched libbar/1.1.0 \[t2.\]% + %unpacked libbar/1.1.0 \[t2.\]% + %configured libbaz/1.1.0 \[t2..bpkg.host.\]% + %configured foo/1.1.0 \[t2..bpkg.host.\]% + %configured libbaz/1.1.0 \[t2.\]% + %configured libbar/1.1.0 \[t2.\]% + %info: t2.+libbar-1.1.0.+ is up to date% + %updated libbar/1.1.0 \[t2.\]% + EOE + + $pkg_status -d t1 -r >>/EOO + !libbar configured 1.0.0 available 1.1.0 + foo [t1/.bpkg/host/] configured 1.0.0 available 1.1.0 + libbaz [t1/.bpkg/host/] configured 1.0.0 available 1.1.0 + libbaz configured 1.0.0 available 1.1.0 + !libbar [t2/] configured 1.1.0 + foo [t2/.bpkg/host/] configured 1.1.0 + libbaz [t2/.bpkg/host/] configured 1.1.0 + libbaz [t2/] configured 1.1.0 + EOO + } + + : repointed-dependent-indirect-dependency-upgrade + : + { + $cfg_create -d t1 --name t1 &t1/***; + $cfg_create -d t2 --name t2 &t2/***; + $cfg_create -d h1 --name h1 --type host &h1/***; + + $cfg_add -d t1 t2 2>!; + $cfg_add -d t1 h1 2>!; + $cfg_add -d t2 h1 2>!; + + $rep_add -d t1 $rep/t7a && $rep_fetch -d t1; + + $* -d t1 libfix --yes 2>!; + + $pkg_status -d t1 -r >>/EOO; + !libfix configured 1.0.0 + libbar configured 1.0.0 + foo [h1/] configured 1.0.0 + libbaz [h1/] configured 1.0.0 + libbaz configured 1.0.0 + libbox configured 1.0.0 + foo [h1/] configured 1.0.0 + libbaz [h1/] configured 1.0.0 + libbaz configured 1.0.0 + libfax configured 1.0.0 + EOO + + $rep_add -d t1 $rep/t7b && $rep_fetch -d t1; + + $* -d t1 libfix libfax +{ --config-name t2 } foo +{ --config-name h1 } <<EOI 2>>~%EOE%; + y + y + EOI + % new libfax/1.0.0 \[t2.\]% + % upgrade libbaz/1.1.0 \[h1.\] \(required by foo \[h1.\]\)% + % upgrade foo/1.1.0 \[h1.\]% + % reconfigure libbox \(dependent of foo \[h1.\]\)% + % reconfigure libbar \(dependent of foo \[h1.\]\)% + % drop libfax/1.0.0 \(unused\)% + reconfigure/update libfix/1.0.0 + continue? [Y/n] update dependent packages? [Y/n] disfigured libfix/1.0.0 + disfigured libfax/1.0.0 + disfigured libbar/1.0.0 + disfigured libbox/1.0.0 + %disfigured foo/1.0.0 \[h1.\]% + %disfigured libbaz/1.0.0 \[h1.\]% + %fetched libfax/1.0.0 \[t2.\]% + %unpacked libfax/1.0.0 \[t2.\]% + %fetched libbaz/1.1.0 \[h1.\]% + %unpacked libbaz/1.1.0 \[h1.\]% + %fetched foo/1.1.0 \[h1.\]% + %unpacked foo/1.1.0 \[h1.\]% + purged libfax/1.0.0 + %configured libfax/1.0.0 \[t2.\]% + %configured libbaz/1.1.0 \[h1.\]% + %configured foo/1.1.0 \[h1.\]% + configured libbox/1.0.0 + configured libbar/1.0.0 + configured libfix/1.0.0 + %info: t2.+libfax-1.0.0.+ is up to date% + %info: h1.+foo-1.1.0.+ is up to date% + %info: t1.+libfix-1.0.0.+ is up to date% + %info: t1.+libbox-1.0.0.+ is up to date% + %info: t1.+libbar-1.0.0.+ is up to date% + %updated libfax/1.0.0 \[t2.\]% + %updated foo/1.1.0 \[h1.\]% + updated libfix/1.0.0 + updated libbox/1.0.0 + updated libbar/1.0.0 + EOE + + $pkg_status -d t1 -r >>/EOO + !libfix configured 1.0.0 + libbar configured 1.0.0 available 1.1.0 + !foo [h1/] configured 1.1.0 + libbaz [h1/] configured 1.1.0 + libbaz configured 1.0.0 available 1.1.0 + libbox configured 1.0.0 available 1.1.0 + !foo [h1/] configured 1.1.0 + libbaz [h1/] configured 1.1.0 + libbaz configured 1.0.0 available 1.1.0 + !libfax [t2/] configured 1.0.0 + !libfax [t2/] configured 1.0.0 + !foo [h1/] configured 1.1.0 + libbaz [h1/] configured 1.1.0 + EOO + } + } } |