From 22165c649ca2c5ef216ae3f99fbfb2dc0fff99ab Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Mon, 30 Aug 2021 23:24:52 +0300 Subject: Make pkg-build to accept multiple current configurations --- bpkg/bpkg.cxx | 11 + bpkg/database.cxx | 31 +- bpkg/database.hxx | 56 +- bpkg/package.cxx | 4 +- bpkg/pkg-build.cli | 39 +- bpkg/pkg-build.cxx | 989 ++++++++++++++++++---------- tests/common/linked/t7a/libbaz-1.0.0.tar.gz | Bin 354 -> 410 bytes tests/pkg-build.testscript | 413 ++++++++++-- tests/pkg-drop.testscript | 21 +- 9 files changed, 1109 insertions(+), 455 deletions(-) diff --git a/bpkg/bpkg.cxx b/bpkg/bpkg.cxx index 49f4055..953a5ad 100644 --- a/bpkg/bpkg.cxx +++ b/bpkg/bpkg.cxx @@ -145,6 +145,17 @@ namespace bpkg main (int argc, char* argv[]); } +// Note that pkg-build command supports multiple configurations and +// initializes multiple temporary directories itself. This function is, +// however, required since pkg_build_options::directory() returns a vector and +// the below template function cannot be used. +// +static inline const dir_path& +cfg_dir (const pkg_build_options*) +{ + return empty_dir_path; +} + // Get -d|--directory value if the option class O has it and empty path // otherwise. Note that for some commands (like rep-info) that allow // specifying empty path, the returned value is a string, not a dir_path. diff --git a/bpkg/database.cxx b/bpkg/database.cxx index 863ada4..ab2bcfc 100644 --- a/bpkg/database.cxx +++ b/bpkg/database.cxx @@ -223,7 +223,8 @@ namespace bpkg odb::tracer& tr, bool pre_attach, bool sys_rep, - const dir_paths& pre_link) + const dir_paths& pre_link, + std::string str_repr) : sqlite::database ( cfg_path (d, create != nullptr).string (), SQLITE_OPEN_READWRITE | (create != nullptr ? SQLITE_OPEN_CREATE : 0), @@ -232,7 +233,8 @@ namespace bpkg unique_ptr ( new sqlite::serial_connection_factory)), // Single connection. config (normalize (d, "configuration")), - config_orig (d) + config_orig (d), + string (move (str_repr)) { bpkg::tracer trace ("database"); @@ -382,6 +384,9 @@ namespace bpkg else config_orig = config; + + string = '[' + config_orig.representation () + ']'; + try { tracer_guard tg (*this, trace); @@ -428,6 +433,7 @@ namespace bpkg type (move (db.type)), config (move (db.config)), config_orig (move (db.config_orig)), + string (move (db.string)), system_repository (move (db.system_repository)), impl_ (db.impl_), explicit_links_ (move (db.explicit_links_)), @@ -996,15 +1002,24 @@ namespace bpkg return r->db; } - database& database:: - find_dependency_config (const uuid_type& uid) + database* database:: + try_find_dependency_config (const uuid_type& uid) { for (database& ldb: dependency_configs ()) { if (uid == ldb.uuid) - return ldb; + return &ldb; } + return nullptr; + } + + database& database:: + find_dependency_config (const uuid_type& uid) + { + if (database* db = try_find_dependency_config (uid)) + return *db; + fail << "no configuration with uuid " << uid << " is linked with " << config_orig << endf; } @@ -1050,10 +1065,4 @@ namespace bpkg { return *this == main_database (); } - - string database:: - string () - { - return main () ? empty_string : '[' + config_orig.representation () + ']'; - } } diff --git a/bpkg/database.hxx b/bpkg/database.hxx index bc3ac08..40474d3 100644 --- a/bpkg/database.hxx +++ b/bpkg/database.hxx @@ -108,11 +108,21 @@ namespace bpkg // treated as linked configurations for schema migration purposes. If // specified, these paths should be absolute and normalized. // + // Optionally, specify the database string representation for use in + // diagnostics. + // database (const dir_path& cfg, const shared_ptr& self, odb::tracer& tr, - const dir_paths& pre_link = dir_paths ()) - : database (cfg, self.get (), tr, false, false, pre_link) + const dir_paths& pre_link = dir_paths (), + std::string str_repr = "") + : database (cfg, + self.get (), + tr, + false, + false, + pre_link, + move (str_repr)) { assert (self != nullptr); } @@ -132,8 +142,17 @@ namespace bpkg odb::tracer& tr, bool pre_attach, bool sys_rep = false, - const dir_paths& pre_link = dir_paths ()) - : database (cfg, nullptr, tr, pre_attach, sys_rep, pre_link) {} + const dir_paths& pre_link = dir_paths (), + std::string str_repr = "") + : database (cfg, + nullptr, + tr, + pre_attach, + sys_rep, + pre_link, + move (str_repr)) + { + } ~database (); @@ -324,6 +343,11 @@ namespace bpkg database& find_dependency_config (const uuid_type&); + // As above but return NULL if not found, rather then failing. + // + database* + try_find_dependency_config (const uuid_type&); + // Return true if this configuration is private (i.e. its parent directory // name is `.bpkg`). // @@ -346,15 +370,6 @@ namespace bpkg database* private_config (const string& type); - // Return an empty string for the main database and the original - // configuration directory path in the `[]` form otherwise. - // - // NOTE: remember to update pkg_command_vars::string() if changing the - // format. - // - std::string - string (); - // Verify that the link information (uuid, type, etc) matches the linked // configuration. Issue diagnostics and fail if that's not the case. // @@ -394,6 +409,16 @@ namespace bpkg // dir_path config_orig; + // The database string representation for use in diagnostics. + // + // By default it is empty for the main database and the original + // configuration directory path in the `[]` form otherwise. + // + // NOTE: remember to update pkg_command_vars::string() and pkg-build.cxx + // if changing the format. + // + std::string string; + // Per-configuration system repository (only loaded if sys_rep constructor // argument is true). // @@ -409,7 +434,8 @@ namespace bpkg odb::tracer&, bool pre_attach, bool sys_rep, - const dir_paths& pre_link); + const dir_paths& pre_link, + std::string str_repr); // Create attached database. // @@ -494,7 +520,7 @@ namespace bpkg inline ostream& operator<< (ostream& os, const database& db) { - string s (const_cast (db).string ()); + const string& s (const_cast (db).string); if (!s.empty ()) os << ' ' << s; diff --git a/bpkg/package.cxx b/bpkg/package.cxx index e458a86..894051a 100644 --- a/bpkg/package.cxx +++ b/bpkg/package.cxx @@ -56,7 +56,7 @@ namespace bpkg string config_package:: string () const { - std::string s (db.string ()); + const std::string& s (db.string); return !s.empty () ? name.string () + ' ' + s : name.string (); } @@ -490,7 +490,7 @@ namespace bpkg string selected_package:: string (database& db) const { - std::string s (db.string ()); + const std::string& s (db.string); return !s.empty () ? string () + ' ' + s : string (); } diff --git a/bpkg/pkg-build.cli b/bpkg/pkg-build.cli index 2ae2948..6667053 100644 --- a/bpkg/pkg-build.cli +++ b/bpkg/pkg-build.cli @@ -1,7 +1,7 @@ // file : bpkg/pkg-build.cli // license : MIT; see accompanying LICENSE file -include ; +include ; "\section=1" "\name=bpkg-pkg-build" @@ -255,29 +255,32 @@ namespace bpkg \l{bpkg-pkg-checkout(1)} for details." } - string --config-name + strings --config-name { - "", + "", "Name of the linked configuration to build this package(s) in. By - default, the package is built in the current configuration." + default, the package is built in the current configuration. Repeat + this option to specify multiple configurations." } - uint64_t --config-id + vector --config-id { - "", + "", "Numeric id of the linked configuration to build this package(s) in. By - default, the package is built in the current configuration." + default, the package is built in the current configuration. Repeat this + option to specify multiple configurations." } - uuid --config-uuid + vector --config-uuid { "", "UUID of the linked configuration to build this package(s) in. By - default, the package is built in the current configuration." + default, the package is built in the current configuration. Repeat this + this option to specify multiple configurations." } }; - class pkg_build_options: configuration_options, + class pkg_build_options: common_options, pkg_build_pkg_options { "\h|PKG-BUILD GLOBAL OPTIONS|" @@ -379,6 +382,22 @@ namespace bpkg See \l{bpkg-cfg-create(1)} for details on linked configurations." } + + bool --no-move + { + "Don't move dependency packages between configurations. In this mode the + \cb{--config-*} options specify packages' current rather than new + locations." + } + + dir_paths --directory|-d + { + "", + "Assume current configuration is in rather than in the current + working directory. Repeat this option to specify multiple current + configurations. If multiple configurations are specified, they must + belong to the same linked configuration cluster." + } }; " diff --git a/bpkg/pkg-build.cxx b/bpkg/pkg-build.cxx index 66c9c36..19f351f 100644 --- a/bpkg/pkg-build.cxx +++ b/bpkg/pkg-build.cxx @@ -43,9 +43,27 @@ namespace bpkg // - Configuration vars (both passed and preserved) // + // Current configurations as specified with --directory|-d (or the current + // working directory if none specified). + // + static linked_databases current_configs; + + static inline bool + multi_config () + { + return current_configs.size () != 1; + } + + static inline bool + current (database& db) + { + return find (current_configs.begin (), current_configs.end (), db) != + current_configs.end (); + } + // Configurations to use as the repository information sources. // - // The list contains the current configuration and configurations of the + // The list contains the current configurations and configurations of the // specified on the command line build-to-hold packages (ultimate // dependents). // @@ -396,27 +414,13 @@ namespace bpkg return r; } - // Compare two shared pointers via the pointed-to object addresses. - // - struct compare_shared_ptr - { - template - bool - operator() (const P& x, const P& y) const - { - return x.get () < y.get (); - } - }; - - // The current configuration dependents being "repointed" to prerequisites + // The current configurations dependents being "repointed" to prerequisites // in other configurations, together with their replacement flags. The flag // is true for the replacement prerequisites ("new") and false for the // prerequisites being replaced ("old"). The unamended prerequisites have no // entries. // - using repointed_dependents = map, - map, - compare_shared_ptr>; + using repointed_dependents = map>; // List of the private configuration paths, relative to the containing // configuration directories (.bpkg/host/, etc), together with the @@ -662,7 +666,7 @@ namespace bpkg string available_name_version_db () const { - string s (db.get ().string ()); + const string& s (db.get ().string); return !s.empty () ? available_name_version () + ' ' + s : available_name_version (); @@ -711,7 +715,7 @@ namespace bpkg // Propagate the user-selection tag. // - required_by.emplace (db.get ().main_database (), package_name ()); + required_by.emplace (db.get ().main_database (), ""); } // Copy the required-by package names only if semantics matches. @@ -1030,6 +1034,7 @@ namespace bpkg if (pkg.system) return; + database& pdb (pkg.db); const shared_ptr& sp (pkg.selected); // True if this is an up/down-grade. @@ -1050,7 +1055,9 @@ namespace bpkg { ud = sp->version != pkg.available_version (); - repointed_dependents::const_iterator i (rpt_depts.find (sp)); + repointed_dependents::const_iterator i ( + rpt_depts.find (config_package {pdb, sp->name})); + if (i != rpt_depts.end ()) rpt_prereq_flags = &i->second; @@ -1085,8 +1092,6 @@ namespace bpkg const lazy_shared_ptr& af (pkg.repository_fragment); const package_name& name (ap->id.name); - database& pdb (pkg.db); - for (const dependency_alternatives_ex& da: ap->dependencies) { if (da.conditional) // @@ TODO @@ -1678,7 +1683,6 @@ namespace bpkg void collect_repointed_dependents ( const pkg_build_options& o, - database& mdb, const repointed_dependents& rpt_depts, build_packages::postponed_packages& postponed, const function& fdb, @@ -1686,9 +1690,10 @@ namespace bpkg { for (const auto& rd: rpt_depts) { - const shared_ptr& sp (rd.first); + database& db (rd.first.db); + const package_name& nm (rd.first.name); - auto i (map_.find (mdb, sp->name)); + auto i (map_.find (db, nm)); if (i != map_.end ()) { build_package& b (i->second.package); @@ -1703,10 +1708,12 @@ namespace bpkg } } + shared_ptr sp (db.load (nm)); + // The repointed dependent can be an orphan, so just create the // available package from the selected package. // - auto rp (make_available (o, mdb, sp)); + auto rp (make_available (o, db, sp)); // Add the prerequisite replacements as the required-by packages. // @@ -1722,7 +1729,7 @@ namespace bpkg build_package p { build_package::build, - mdb, + db, sp, move (rp.first), move (rp.second), @@ -1996,11 +2003,8 @@ namespace bpkg if (i != map_.end () && i->second.position != end ()) { - build_package& dp (i->second.package); - - const shared_ptr& dsp (dp.selected); - - repointed_dependents::const_iterator j (rpt_depts.find (sp)); + repointed_dependents::const_iterator j ( + rpt_depts.find (config_package {ddb, dn})); if (j != rpt_depts.end ()) { @@ -2012,6 +2016,9 @@ namespace bpkg continue; } + build_package& dp (i->second.package); + const shared_ptr& dsp (dp.selected); + // 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 @@ -2604,6 +2611,7 @@ namespace bpkg const optional& desired, bool desired_sys, database& desired_db, + const shared_ptr& desired_db_sp, bool patch, bool explicitly, const config_repo_fragments&, @@ -2615,12 +2623,13 @@ namespace bpkg // case we recommend to drop the dependency. // // Note that the user expectations are only applied for dependencies that - // have dependents in the current configuration. + // have dependents in the current configurations. // static optional evaluate_dependency (database& db, const shared_ptr& sp, const dependency_packages& deps, + bool no_move, bool ignore_unsatisfiable) { tracer trace ("evaluate_dependency"); @@ -2629,41 +2638,113 @@ namespace bpkg const package_name& nm (sp->name); - database& mdb (db.main_database ()); + auto no_change = [&db] () + { + return evaluate_result {db, + nullptr /* available */, + nullptr /* repository_fragment */, + false /* unused */, + false /* system */}; + }; // Only search for the user expectations regarding this dependency if it - // has dependents in the current configuration. + // has dependents in the current configurations, unless --no-move is + // specified. + // + // In the no-move mode consider the user-specified configurations not as a + // dependency new location, but as the current location of the dependency + // to which the expectations are applied. Note that multiple package specs + // for the same dependency in different configurations can be specified on + // the command line. // - 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); + linked_databases cur_dbs; + dependency_packages::const_iterator i (deps.end ()); + + if (!no_move) + { + // Collect the current configurations which contain dependents for this + // dependency and assume no expectations if there is none. + // + for (database& cdb: current_configs) + { + if (!query_dependents (cdb, nm, db).empty ()) + cur_dbs.push_back (cdb); + } + + // Search for the user expectations regarding this dependency by + // matching the name and configuration type and fail if there are + // multiple candidates. + // + if (!cur_dbs.empty ()) + { + for (dependency_packages::const_iterator j (deps.begin ()); + j != deps.end (); + ++j) + { + if (j->name == nm && j->db.type == db.type) + { + if (i == deps.end ()) + i = j; + else + fail << "multiple " << db.type << " configurations specified " + << "for dependency package " << nm << + info << i->db.config_orig << + info << j->db.config_orig; + } + } + } + } + else + { + i = find_if (deps.begin (), deps.end (), + [&db, &nm] (const dependency_package& i) + { + return i.name == nm && i.db == db; + }); + } + + bool user_exp (i != deps.end ()); 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. + // Collect the dependents for checking the version constraints, using + // their repository fragments for discovering available dependency package + // versions, etc. // - linked_databases dbs (copy_dep - ? linked_databases ({mdb}) - : db.dependent_configs ()); + // Note that if dependency needs to be copied, then we only consider its + // dependents in the current configurations which potentially can be + // repointed to it. Note that configurations of such dependents must + // contain the new dependency configuration in their dependency tree. + // + linked_databases dep_dbs; + + if (copy_dep) + { + for (database& db: i->db.dependent_configs ()) + { + if (find (cur_dbs.begin (), cur_dbs.end (), db) != cur_dbs.end ()) + dep_dbs.push_back (db); + } + // Bail out if no dependents can be repointed to the dependency. + // + if (dep_dbs.empty ()) + { + l5 ([&]{trace << *sp << db << ": can't repoint";}); + return no_change (); + } + } + else + dep_dbs = db.dependent_configs (); + + // Collect the dependents but bail out if the dependency is used but there + // are no user expectations regarding it. + // vector> pds; - for (database& ddb: dbs) + for (database& ddb: dep_dbs) { - auto ds (ddb.main () ? move (mdb_deps) : query_dependents (ddb, nm, db)); + auto ds (query_dependents (ddb, nm, db)); - // Bail out if the dependency is used but there are no user expectations - // regrading it. - // if (!ds.empty ()) { if (!user_exp) @@ -2687,34 +2768,41 @@ namespace bpkg false /* system */}; } - // If the selected package matches the user expectations then no package - // change is required. + // The requested dependency database, version constraint, and system flag. // - const version& sv (sp->version); - bool ssys (sp->system ()); + assert (i != deps.end ()); - // The requested dependency version constraint and system flag. - // + database& ddb (i->db); const optional& 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)) && - db == ddb) + // The selected package in the desired database which we copy over. + // + // It is the current dependency package, if we don't copy, and may or may + // not exist otherwise. + // + shared_ptr dsp (db == ddb + ? sp + : ddb.find (nm)); + + // If a package in the desired database is already selected and matches + // the user expectations then no package change is required. + // + if (dsp != nullptr && dvc) { - l5 ([&]{trace << *sp << db << ": unchanged";}); + const version& sv (dsp->version); + bool ssys (dsp->system ()); - return evaluate_result {db, - nullptr /* available */, - nullptr /* repository_fragment */, - false /* unused */, - false /* system */}; + if (ssys == dsys && + (ssys ? sv == *dvc->min_version : satisfies (sv, dvc))) + { + l5 ([&]{trace << *dsp << ddb << ": unchanged";}); + return no_change (); + } } - // Build a set of repository fragments the dependent packages now come - // from. Also cache the dependents and the constraints they apply to this + // Build a set of repository fragments the dependent packages come from. + // Also cache the dependents and the constraints they apply to this // dependency. // config_repo_fragments repo_frags; @@ -2725,15 +2813,14 @@ namespace bpkg database& ddb (pd.first); package_dependent& dep (pd.second); - shared_ptr dsp ( - ddb.load (dep.name)); + shared_ptr p (ddb.load (dep.name)); add_dependent_repo_fragments ( ddb, - available_package_id (dsp->name, dsp->version), + available_package_id (p->name, p->version), repo_frags); - dependents.emplace_back (ddb, move (dsp), move (dep.constraint)); + dependents.emplace_back (ddb, move (p), move (dep.constraint)); } return evaluate_dependency (db, @@ -2741,6 +2828,7 @@ namespace bpkg dvc, dsys, ddb, + dsp, i->patch, true /* explicitly */, repo_frags, @@ -2777,6 +2865,7 @@ namespace bpkg const optional& dvc, bool dsys, database& ddb, + const shared_ptr& dsp, bool patch, bool explicitly, const config_repo_fragments& rfs, @@ -2786,7 +2875,6 @@ namespace bpkg tracer trace ("evaluate_dependency"); const package_name& nm (sp->name); - const version& sv (sp->version); auto no_change = [&db] () { @@ -2836,16 +2924,15 @@ namespace bpkg vector> unsatisfiable; bool stub (false); - bool ssys (sp->system ()); assert (!dsys || - (db.system_repository && - db.system_repository->find (nm) != nullptr)); + (ddb.system_repository && + ddb.system_repository->find (nm) != nullptr)); for (auto& af: afs) { shared_ptr& ap (af.first); - const version& av (!dsys ? ap->version : *ap->system_version (db)); + const version& av (!dsys ? ap->version : *ap->system_version (ddb)); // If we aim to upgrade to the latest version and it tends to be less // then the selected one, then what we currently have is the best that @@ -2853,16 +2940,16 @@ namespace bpkg // // Note that we also handle a package stub here. // - if (!dvc && av < sv && db == ddb) + if (!dvc && dsp != nullptr && av < dsp->version) { assert (!dsys); // Version can't be empty for the system package. // For the selected system package we still need to pick a source // package version to downgrade to. // - if (!ssys) + if (!dsp->system ()) { - l5 ([&]{trace << *sp << db << ": best";}); + l5 ([&]{trace << *dsp << ddb << ": best";}); return no_change (); } @@ -2917,9 +3004,9 @@ 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 && db == ddb) + if (dsp != nullptr && av == dsp->version && dsp->system () == dsys) { - l5 ([&]{trace << *sp << db << ": unchanged";}); + l5 ([&]{trace << *dsp << ddb << ": unchanged";}); return no_change (); } @@ -2934,11 +3021,11 @@ namespace bpkg // 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 && db == ddb) + if (!dvc && dsp != nullptr && !dsp->system ()) { assert (!dsys); // Version cannot be empty for the system package. - l5 ([&]{trace << *sp << db << ": only";}); + l5 ([&]{trace << *dsp << ddb << ": only";}); return no_change (); } @@ -2966,7 +3053,9 @@ namespace bpkg if (!dvc && patch) { - assert (ssys); // Otherwise, we would bail out earlier (see above). + // Otherwise, we should have bailed out earlier (see above). + // + assert (dsp != nullptr && dsp->system ()); // Patch (as any upgrade) of a system package is always explicit, so // we always fail and never treat the package as being up to date. @@ -2984,7 +3073,7 @@ namespace bpkg // Note that we don't advise to "build" the package as a system one as // it is already as such (see above). // - assert (!dvc && !dsys && ssys); + assert (!dvc && !dsys && dsp != nullptr && dsp->system ()); fail << package_string (nm, dvc) << ddb << " is not available in " << "source from its dependents' repositories"; @@ -3129,10 +3218,9 @@ namespace bpkg { for (auto& pd: query_dependents_cache (ddb, sp->name, db)) { - shared_ptr dsp ( - ddb.load (pd.name)); + shared_ptr p (ddb.load (pd.name)); - dependents.emplace_back (ddb, dsp, move (pd.constraint)); + dependents.emplace_back (ddb, p, move (pd.constraint)); if (optional u = upgrade_dependencies (ddb, pd.name, recs)) { @@ -3148,7 +3236,7 @@ namespace bpkg // add_dependent_repo_fragments ( ddb, - available_package_id (dsp->name, dsp->version), + available_package_id (p->name, p->version), repo_frags); } } @@ -3167,6 +3255,7 @@ namespace bpkg nullopt /* desired */, false /*desired_sys */, db, + sp, !*upgrade /* patch */, false /* explicitly */, repo_frags, @@ -3217,10 +3306,18 @@ namespace bpkg dr << fail << "multiple --(upgrade|patch)-(immediate|recursive) " << "specified"; - if (((o.config_id_specified () ? 1 : 0) + - (o.config_name_specified () ? 1 : 0) + - (o.config_uuid_specified () ? 1 : 0)) > 1) - dr << fail << "multiple --config-* specified"; + if (multi_config ()) + { + if (const char* opt = o.config_name_specified () ? "--config-name" : + o.config_id_specified () ? "--config-id" : + nullptr) + { + dr << fail << opt << " specified for multiple current " + << "configurations" << + info << "use --config-uuid to specify configurations in " + << "this mode"; + } + } if (!dr.empty () && !pkg.empty ()) dr << info << "while validating options for " << pkg; @@ -3264,27 +3361,31 @@ namespace bpkg dst.checkout_purge (src.checkout_purge () || dst.checkout_purge ()); - if (!dst.config_id_specified () && - !dst.config_name_specified () && - !dst.config_uuid_specified ()) + if (src.config_id_specified ()) { - if (src.config_id_specified ()) - { - dst.config_id (src.config_id ()); - dst.config_id_specified (true); - } + const vector& s (src.config_id ()); + vector& d (dst.config_id ()); + d.insert (d.end (), s.begin (), s.end ()); - if (src.config_name_specified ()) - { - dst.config_name (src.config_name ()); - dst.config_name_specified (true); - } + dst.config_id_specified (true); + } - if (src.config_uuid_specified ()) - { - dst.config_uuid (src.config_uuid ()); - dst.config_uuid_specified (true); - } + if (src.config_name_specified ()) + { + const strings& s (src.config_name ()); + strings& d (dst.config_name ()); + d.insert (d.end (), s.begin (), s.end ()); + + dst.config_name_specified (true); + } + + if (src.config_uuid_specified ()) + { + const vector& s (src.config_uuid ()); + vector& d (dst.config_uuid ()); + d.insert (d.end (), s.begin (), s.end ()); + + dst.config_uuid_specified (true); } } @@ -3302,10 +3403,7 @@ namespace bpkg x.patch_immediate () == y.patch_immediate () && x.patch_recursive () == y.patch_recursive () && x.checkout_root () == y.checkout_root () && - x.checkout_purge () == y.checkout_purge () && - x.config_id () == y.config_id () && - x.config_name () == y.config_name () && - x.config_uuid () == y.config_uuid (); + x.checkout_purge () == y.checkout_purge (); } int @@ -3313,18 +3411,20 @@ namespace bpkg { tracer trace ("pkg_build"); - const dir_path& c (o.directory ()); - l4 ([&]{trace << "configuration: " << c;}); + dir_paths cs; + const dir_paths& config_dirs (!o.directory ().empty () + ? o.directory () + : cs); + + if (config_dirs.empty ()) + cs.push_back (current_dir); + + l4 ([&]{for (const auto& d: config_dirs) trace << "configuration: " << d;}); // Make sure that potential stdout writing failures can be detected. // cout.exceptions (ostream::badbit | ostream::failbit); - // @@ Should we also validate the --no-private-config value, if specified - // (>2 and belongs to the "usable exit code range"). - // - validate_options (o, ""); // Global package options. - if (o.noop_exit_specified ()) { if (o.print_only ()) @@ -3345,9 +3445,53 @@ namespace bpkg fail << "package name argument expected" << info << "run 'bpkg help pkg-build' for more information"; + // If multiple current configurations are specified, then open the first + // one, attach the remaining, verify that their schemas match (which may + // not be the case if they don't belong to the same linked database + // cluster), and attach their explicitly linked databases, recursively. + // // Also populates the system repository. // - database mdb (c, trace, true /* pre_attach */, true /* sys_rep */); + // @@ Note that currently we don't verify the specified configurations + // belong to the same cluster. + // + database mdb (config_dirs[0], + trace, + true /* pre_attach */, + true /* sys_rep */, + dir_paths () /* pre_link */, + (config_dirs.size () == 1 + ? empty_string + : '[' + config_dirs[0].representation () + ']')); + + current_configs.push_back (mdb); + + if (config_dirs.size () != 1) + { + transaction t (mdb); + + odb::schema_version sv (mdb.schema_version ()); + for (auto i (config_dirs.begin () + 1); i != config_dirs.end (); ++i) + { + database& db (mdb.attach (normalize (*i, "configuration"), + true /* sys_rep */)); + + if (db.schema_version () != sv) + fail << "specified configurations belong to different linked " + << "configuration clusters" << + info << mdb.config_orig << + info << db.config_orig; + + db.attach_explicit (true /* sys_rep */); + + // Suppress duplicates. + // + if (!current (db)) + current_configs.push_back (db); + } + } + + validate_options (o, ""); // Global package options. // Note that the session spans all our transactions. The idea here is that // selected_package objects in build_packages below will be cached in this @@ -3372,18 +3516,19 @@ namespace bpkg // their ultimate dependent configurations. // // Also collect the databases specified on the command line for the held - // packages, to later use them as a repository information sources for the - // dependencies. + // packages, to later use them as repository information sources for the + // dependencies. Additionally use the current configurations as repository + // information sources. // - repo_configs.push_back (mdb); + repo_configs = current_configs; struct pkg_spec { - database* db; // A pointer since we build these objects incrementally. - string packages; - repository_location location; - pkg_options options; - strings config_vars; + reference_wrapper db; + string packages; + repository_location location; + pkg_options options; + strings config_vars; }; vector specs; @@ -3446,28 +3591,25 @@ namespace bpkg fail << "unexpected configuration variable '" << a << "'" << info << "use the '--' separator to treat it as a package"; - specs.emplace_back (); - pkg_spec& ps (specs.back ()); + pkg_options po; + + // Merge the common and package-specific configuration variables + // (commons go first). + // + strings cvs (cvars); try { - auto& po (ps.options); - cli::scanner& ag (args.group ()); po.parse (ag, cli::unknown_mode::fail, cli::unknown_mode::stop); - // Merge the common and package-specific configuration variables - // (commons go first). - // - ps.config_vars = cvars; - while (ag.more ()) { string a (ag.next ()); if (a.find ('=') == string::npos) fail << "unexpected group argument '" << a << "'"; - ps.config_vars.push_back (move (a)); + cvs.push_back (move (a)); } // We have to manually merge global options into local since just @@ -3480,33 +3622,82 @@ namespace bpkg } catch (const cli::exception& e) { - fail << e << " grouped for argument '" << a << "'"; + fail << e << " grouped for argument " << a; } - // Note: main database if no --config-* option is specified. + // Resolve the configuration options into the databases, suppressing + // duplicates. // - if (ps.options.config_name_specified ()) - ps.db = &mdb.find_attached (ps.options.config_name ()); - else if (ps.options.config_uuid_specified ()) - ps.db = &mdb.find_dependency_config (ps.options.config_uuid ()); - else - ps.db = &mdb.find_attached (ps.options.config_id ()); + // Note: main database if no --config-* option is specified, unless we + // are in the multi-config mode, in which case we fail. + // + linked_databases dbs; + auto add_db = [&dbs] (database& db) + { + if (find (dbs.begin (), dbs.end (), db) == dbs.end ()) + dbs.push_back (db); + }; + + for (const string& nm: po.config_name ()) + { + assert (!multi_config ()); // Should have failed earlier. + add_db (mdb.find_attached (nm)); + } + + for (uint64_t id: po.config_id ()) + { + assert (!multi_config ()); // Should have failed earlier. + add_db (mdb.find_attached (id)); + } + + for (const uuid& uid: po.config_uuid ()) + { + database* db (nullptr); + + for (database& cdb: current_configs) + { + if ((db = cdb.try_find_dependency_config (uid)) != nullptr) + break; + } + + if (db == nullptr) + fail << "no configuration with uuid " << uid << " is linked with " + << (!multi_config () + ? mdb.config_orig.representation () + : "specified current configurations"); + + add_db (*db); + } + + if (dbs.empty ()) + { + if (multi_config ()) + fail << "no configuration specified for " << a << + info << "configuration must be explicitly specified for each " + << "package in multi-configurations mode" << + info << "use --config-uuid to specify its configuration"; + + dbs.push_back (mdb); + } if (!a.empty () && a[0] == '?') { - ps.options.dependency (true); + po.dependency (true); a.erase (0, 1); } - database& pdb (*ps.db); - - // If this is a package to hold, then add its database to the + // If this is a package to hold, then add its databases to the // repository information source list, suppressing duplicates. // - if (!ps.options.dependency () && - find (repo_configs.begin (), repo_configs.end (), pdb) == - repo_configs.end ()) - repo_configs.push_back (pdb); + if (!po.dependency ()) + { + for (database& db: dbs) + { + if (find (repo_configs.begin (), repo_configs.end (), db) == + repo_configs.end ()) + repo_configs.push_back (db); + } + } // Check if the argument has the []@ form or looks // like a URL. Find the position of if that's the case and @@ -3548,98 +3739,141 @@ namespace bpkg if (l.empty ()) fail << "empty repository location in '" << a << "'"; - if (ps.options.dependency ()) + if (po.dependency ()) fail << "unexpected repository location in '?" << a << "'" << info << "repository location cannot be specified for " << "dependencies"; - // Search for the repository location in the database before trying - // to parse it. Note that the straight parsing could otherwise fail, - // being unable to properly guess the repository type. - // - // Also note that the repository location URL is not unique and we - // can potentially end up with multiple repositories. For example: - // - // $ bpkg add git+file:/path/to/git/repo dir+file:/path/to/git/repo - // $ bpkg build @/path/to/git/repo - // - // That's why we pick the repository only if there is exactly one - // match. - // - shared_ptr r; + string pks (p > 1 ? string (a, 0, p - 1) : empty_string); + + for (size_t i (0); i != dbs.size (); ++i) { - using query = query; + database& db (dbs[i]); - // For case-insensitive filesystems (Windows) we need to match the - // location case-insensitively against the local repository URLs - // and case-sensitively against the remote ones. + // Search for the repository location in the database before + // trying to parse it. Note that the straight parsing could + // otherwise fail, being unable to properly guess the repository + // type. + // + // Also note that the repository location URL is not unique and we + // can potentially end up with multiple repositories. For example: + // + // $ bpkg add git+file:/path/to/git/repo dir+file:/path/to/git/repo + // $ bpkg build @/path/to/git/repo // - // Note that the root repository will never be matched, since its - // location is empty. + // That's why we pick the repository only if there is exactly one + // match. // - const auto& url (query::location.url); + shared_ptr r; + { + using query = query; + + // For case-insensitive filesystems (Windows) we need to match + // the location case-insensitively against the local repository + // URLs and case-sensitively against the remote ones. + // + // Note that the root repository will never be matched, since + // its location is empty. + // + const auto& url (query::location.url); #ifndef _WIN32 - query q (url == l); + query q (url == l); #else - string u (url.table ()); - u += '.'; - u += url.column (); + string u (url.table ()); + u += '.'; + u += url.column (); - query q ( - (!query::local && url == l) || - ( query::local && u + " COLLATE nocase = " + query::_val (l))); + query q ( + (!query::local && url == l) || + ( query::local && u + " COLLATE nocase = " + query::_val (l))); #endif - auto rs (pdb.query (q)); - auto i (rs.begin ()); + auto rs (db.query (q)); + auto i (rs.begin ()); - if (i != rs.end ()) - { - r = i.load (); + if (i != rs.end ()) + { + r = i.load (); - // Fallback to parsing the location if several repositories - // match. - // - if (++i != rs.end ()) - r = nullptr; + // Fallback to parsing the location if several repositories + // match. + // + if (++i != rs.end ()) + r = nullptr; + } } - } - ps.location = r != nullptr - ? r->location - : parse_location (l, nullopt /* type */); + repository_location loc (r != nullptr + ? r->location + : parse_location (l, nullopt /* type */)); - if (p > 1) - ps.packages = string (a, 0, p - 1); + if (!o.no_fetch ()) + { + auto i (locations.find (db)); + if (i == locations.end ()) + i = locations.insert (db, + vector ()).first; - if (!o.no_fetch ()) - { - auto i (locations.find (pdb)); - if (i == locations.end ()) - i = locations.insert (pdb, - vector ()).first; + auto pr = [&loc] (const repository_location& i) -> bool + { + return i.canonical_name () == loc.canonical_name (); + }; - auto pr = [&ps] (const repository_location& i) -> bool - { - return i.canonical_name () == ps.location.canonical_name (); - }; + vector& ls (i->second); + auto j (find_if (ls.begin (), ls.end (), pr)); - vector& ls (i->second); - auto j (find_if (ls.begin (), ls.end (), pr)); + if (j != ls.end ()) + *j = loc; + else + ls.push_back (loc); + } - if (j != ls.end ()) - *j = ps.location; + // Move the pkg_spec components for the last database on the list, + // rather then copying them. + // + if (i != dbs.size () - 1) + specs.push_back (pkg_spec {db, pks, move (loc), po, cvs}); else - ls.push_back (ps.location); + specs.push_back (pkg_spec {db, + move (pks), + move (loc), + move (po), + move (cvs)}); } } else - ps.packages = move (a); + { + // Move the pkg_spec components for the last database in the list, + // rather then copying them. + // + for (size_t i (0); i != dbs.size (); ++i) + { + database& db (dbs[i]); + + if (i != dbs.size () - 1) + specs.emplace_back (pkg_spec {db, + a, + repository_location (), + po, + cvs}); + else + specs.emplace_back (pkg_spec {db, + move (a), + repository_location (), + move (po), + move (cvs)}); + } + } } t.commit (); + // Initialize tmp directories. + // + for (database& db: repo_configs) + init_tmp (db.config_orig); + // Fetch the repositories in the current configuration. // // Note that during this build only the repositories information from @@ -3819,14 +4053,14 @@ namespace bpkg add_bool ("--checkout-purge", o.checkout_purge ()); - if (o.config_id_specified ()) - add_num ("--config-id", o.config_id ()); + for (const string& nm: o.config_name ()) + add_string ("--config-name", nm); - if (o.config_name_specified ()) - add_string ("--config-name", o.config_name ()); + for (uint64_t id: o.config_id ()) + add_num ("--config-id", id); - if (o.config_uuid_specified ()) - add_string ("--config-uuid", o.config_uuid ().string ()); + for (const uuid& uid: o.config_uuid ()) + add_string ("--config-uuid", uid.string ()); // Compose the option/variable group. // @@ -3917,7 +4151,7 @@ namespace bpkg if (sys && vc) stubs.push_back (make_shared (n)); - pkg_args.push_back (arg_package (*ps.db, + pkg_args.push_back (arg_package (ps.db, sc, move (n), move (vc), @@ -3925,7 +4159,7 @@ namespace bpkg move (ps.config_vars))); } else // Add unparsed. - pkg_args.push_back (arg_raw (*ps.db, + pkg_args.push_back (arg_raw (ps.db, move (ps.packages), move (ps.options), move (ps.config_vars))); @@ -3936,7 +4170,7 @@ namespace bpkg // Use it both as the package database and the source of the // repository information. // - database& pdb (*ps.db); + database& pdb (ps.db); // Expand the [[]@] spec. Fail if the repository // is not found in this configuration, that can be the case in the @@ -4178,14 +4412,14 @@ namespace bpkg // Check if the package is a duplicate. Return true if it is but // harmless. // - map package_map; + map 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 (pa.name, pa)); + auto r (package_map.emplace (config_package {pa.db, pa.name}, pa)); const pkg_arg& a (r.first->second); assert (arg_parsed (a)); @@ -4773,92 +5007,96 @@ namespace bpkg if (hold_pkgs.empty () && dep_pkgs.empty () && (o.upgrade () || o.patch ())) { - lazy_shared_ptr root (mdb, empty_string); - - using query = query; - - for (shared_ptr sp: - pointer_result ( - mdb.query (query::state == "configured" && - query::hold_package))) + for (database& cdb: current_configs) { - // Let's skip upgrading system packages as they are, probably, - // configured as such for a reason. - // - if (sp->system ()) - continue; - const package_name& name (sp->name); + lazy_shared_ptr root (cdb, empty_string); - optional pc; + using query = query; - if (o.patch ()) + for (shared_ptr sp: + pointer_result ( + cdb.query ( + query::state == "configured" && query::hold_package))) { - pc = patch_constraint (sp); - - // Skip the non-patchable selected package. Note that the warning - // have already been issued in this case. + // Let's skip upgrading system packages as they are, probably, + // configured as such for a reason. // - if (!pc) + if (sp->system ()) continue; - } - auto apr (find_available_one (name, pc, root)); + const package_name& name (sp->name); - shared_ptr ap (move (apr.first)); - if (ap == nullptr || ap->stub ()) - { - diag_record dr (fail); - dr << name << " is not available"; + optional pc; - if (ap != nullptr) - dr << " in source" << - info << "consider building it as " - << package_string (name, version (), true /* system */) - << " if it is available from the system"; + if (o.patch ()) + { + pc = patch_constraint (sp); - // Let's help the new user out here a bit. - // - check_any_available (mdb, t, &dr); - } + // Skip the non-patchable selected package. Note that the + // warning have already been issued in this case. + // + if (!pc) + continue; + } - // We will keep the output directory only if the external package is - // replaced with an external one (see above for details). - // - bool keep_out (o.keep_out () && sp->external ()); + auto apr (find_available_one (name, pc, root)); - // @@ Pass pa.configure_only() when support for package-specific - // --configure-only is added. - // - build_package p { - build_package::build, - mdb, - move (sp), - move (ap), - move (apr.second), - true, // Hold package. - false, // Hold version. - {}, // Constraints. - false, // System package. - keep_out, - false, // Configure-only. - nullopt, // Checkout root. - false, // Checkout purge. - strings (), // Configuration variables. - {config_package {mdb, ""}}, // Required by (command line). - false, // Required by dependents. - 0}; // State flags. + shared_ptr ap (move (apr.first)); + if (ap == nullptr || ap->stub ()) + { + diag_record dr (fail); + dr << name << " is not available"; - l4 ([&]{trace << "stashing held package " - << p.available_name_version_db ();}); + if (ap != nullptr) + dr << " in source" << + info << "consider building it as " + << package_string (name, version (), true /* system */) + << " if it is available from the system"; + + // Let's help the new user out here a bit. + // + check_any_available (cdb, t, &dr); + } - hold_pkgs.push_back (move (p)); + // We will keep the output directory only if the external package is + // replaced with an external one (see above for details). + // + bool keep_out (o.keep_out () && sp->external ()); - // If there are also -i|-r, then we are also upgrading dependencies - // of all held packages. - // - if (o.immediate () || o.recursive ()) - rec_pkgs.push_back ( - recursive_package {mdb, name, o.upgrade (), o.recursive ()}); + // @@ Pass pa.configure_only() when support for package-specific + // --configure-only is added. + // + build_package p { + build_package::build, + cdb, + move (sp), + move (ap), + move (apr.second), + true, // Hold package. + false, // Hold version. + {}, // Constraints. + false, // System package. + keep_out, + false, // Configure-only. + nullopt, // Checkout root. + false, // Checkout purge. + strings (), // Configuration variables. + {config_package {mdb, ""}}, // Required by (command line). + false, // Required by dependents. + 0}; // State flags. + + l4 ([&]{trace << "stashing held package " + << p.available_name_version_db ();}); + + hold_pkgs.push_back (move (p)); + + // If there are also -i|-r, then we are also upgrading dependencies + // of all held packages. + // + if (o.immediate () || o.recursive ()) + rec_pkgs.push_back ( + recursive_package {cdb, name, o.upgrade (), o.recursive ()}); + } } } @@ -4893,20 +5131,26 @@ namespace bpkg const package_name& nm, bool buildtime) -> database* { - if (db.main ()) + database* r (nullptr); + + linked_databases ddbs (db.dependency_configs (nm, buildtime)); + + for (const config_package& cp: conf_pkgs) { - auto i (find_if (conf_pkgs.begin (), conf_pkgs.end (), - [&nm] (const config_package& i) - { - return i.name == nm; - })); - - if (i != conf_pkgs.end () && - i->db.type == dependency_type (db, nm, buildtime)) - return &i->db; + if (cp.name == nm && + find (ddbs.begin (), ddbs.end (), cp.db) != ddbs.end ()) + { + if (r == nullptr) + r = &cp.db; + else + fail << "multiple " << cp.db.type << " configurations " + << "specified for package " << nm << + info << r->config_orig << + info << cp.db.config_orig; + } } - return nullptr; + return r; }); // Assemble the list of packages we will need to build-to-hold, still used @@ -4970,7 +5214,7 @@ namespace bpkg vector deps; // Map the repointed dependents to the replacement flags (see - // repointed_dependents for details). + // repointed_dependents for details), unless --no-move is specified. // // Note that the overall plan is to add the replacement prerequisites to // the repointed dependents prerequisites sets at the beginning of the @@ -4981,6 +5225,8 @@ namespace bpkg // appropriate. // repointed_dependents rpt_depts; + + if (!o.no_move ()) { transaction t (mdb); @@ -4988,34 +5234,39 @@ namespace bpkg query q (query::state == "configured"); - for (shared_ptr sp: - pointer_result (mdb.query (q))) + for (database& cdb: current_configs) { - map ps; // Old/new prerequisites. - - for (const auto& p: sp->prerequisites) + for (shared_ptr sp: + pointer_result (cdb.query (q))) { - 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; - })); + map ps; // Old/new prerequisites. - // 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) + for (const auto& p: sp->prerequisites) { - ps.emplace (config_package {i->db, name}, true); - ps.emplace (config_package { db, name}, false); + database& db (p.first.database ()); + const package_name& name (p.first.object_id ()); + + // Note that if a prerequisite is in a configuration of the host + // type, it is not necessarily a build-time dependency (think of + // a dependent from a self-hosted configuration and its runtime + // dependency). However, here it doesn't really matter. + // + database* pdb ( + find_prereq_database (cdb, + name, + (db.type == host_config_type || + db.type == build2_config_type))); + + if (pdb != nullptr && *pdb != db && pdb->type == db.type) + { + ps.emplace (config_package {*pdb, name}, true); + ps.emplace (config_package { db, name}, false); + } } - } - if (!ps.empty ()) - rpt_depts.emplace (move (sp), move (ps)); + if (!ps.empty ()) + rpt_depts.emplace (config_package {cdb, sp->name}, move (ps)); + } } t.commit (); @@ -5039,7 +5290,10 @@ namespace bpkg // for (auto& rd: rpt_depts) { - const shared_ptr& sp (rd.first); + database& db (rd.first.db); + const package_name& nm (rd.first.name); + + shared_ptr sp (db.load (nm)); for (const auto& prq: rd.second) { @@ -5059,7 +5313,7 @@ namespace bpkg } } - mdb.update (sp); + db.update (sp); } // Private configurations that were created during collection of the @@ -5160,7 +5414,6 @@ namespace bpkg // packages from different configurations. // pkgs.collect_repointed_dependents (o, - mdb, rpt_depts, postponed, find_prereq_database, @@ -5265,8 +5518,8 @@ namespace bpkg find_prereq_database); for (const auto& rd: rpt_depts) - pkgs.order (mdb, - rd.first->name, + pkgs.order (rd.first.db, + rd.first.name, nullopt /* buildtime */, find_prereq_database, false /* reorder */); @@ -5296,7 +5549,10 @@ namespace bpkg // for (auto& rd: rpt_depts) { - const shared_ptr& sp (rd.first); + database& db (rd.first.db); + const package_name& nm (rd.first.name); + + shared_ptr sp (db.load (nm)); for (const auto& prq: rd.second) { @@ -5315,7 +5571,7 @@ namespace bpkg } } - mdb.update (sp); + db.update (sp); } // We are about to execute the plan on the database (but not on the @@ -5381,7 +5637,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, &o] ( database& db, const shared_ptr& sp, bool ignore_unsatisfiable = true) -> optional @@ -5391,7 +5647,11 @@ namespace bpkg // See if there is an optional dependency upgrade recommendation. // if (!sp->hold_package) - r = evaluate_dependency (db, sp, dep_pkgs, ignore_unsatisfiable); + r = evaluate_dependency (db, + sp, + dep_pkgs, + o.no_move (), + ignore_unsatisfiable); // If none, then see for the recursive dependency upgrade // recommendation. @@ -5488,7 +5748,7 @@ namespace bpkg // make sure that the unsatisfiable dependency, if left, is // reported. // - auto need_refinement = [&eval_dep, &deps, &rec_pkgs, &mdb, &o] ( + auto need_refinement = [&eval_dep, &deps, &rec_pkgs, &o] ( bool diag = false) -> bool { // Examine the new dependency set for any up/down-grade/drops. @@ -5507,12 +5767,23 @@ namespace bpkg // up/down-grading or dropping packages in configurations that // only contain dependents, some of which we may only reconfigure. // - for (database& ldb: mdb.dependency_configs ()) + linked_databases dbs; + + for (database& cdb: current_configs) + { + for (database& db: cdb.dependency_configs ()) + { + if (find (dbs.begin (), dbs.end (), db) == dbs.end ()) + dbs.push_back (db); + } + } + + for (database& db: dbs) { for (shared_ptr sp: - pointer_result (ldb.query (q))) + pointer_result (db.query (q))) { - if (optional er = eval_dep (ldb, sp, !diag)) + if (optional er = eval_dep (db, sp, !diag)) { // Skip unused if we were instructed to keep them. // @@ -5976,7 +6247,7 @@ namespace bpkg act += ' ' + sp->name.string (); - string s (pdb.string ()); + const string& s (pdb.string); if (!s.empty ()) act += ' ' + s; } @@ -6144,7 +6415,7 @@ namespace bpkg if (!sp->system () && // System package doesn't need update. p.user_selection ()) upkgs.push_back (pkg_command_vars {db.config_orig, - db.main (), + !multi_config () && db.main (), sp, strings () /* vars */, false /* cwd */}); @@ -6165,7 +6436,7 @@ namespace bpkg (*p.action == build_package::build && (p.flags & build_package::build_repoint) != 0)) upkgs.push_back (pkg_command_vars {db.config_orig, - db.main (), + !multi_config () && db.main (), p.selected, strings () /* vars */, false /* cwd */}); diff --git a/tests/common/linked/t7a/libbaz-1.0.0.tar.gz b/tests/common/linked/t7a/libbaz-1.0.0.tar.gz index 723ac32..82d89fa 100644 Binary files a/tests/common/linked/t7a/libbaz-1.0.0.tar.gz and b/tests/common/linked/t7a/libbaz-1.0.0.tar.gz differ diff --git a/tests/pkg-build.testscript b/tests/pkg-build.testscript index 3b4bf5c..9f2ffe0 100644 --- a/tests/pkg-build.testscript +++ b/tests/pkg-build.testscript @@ -922,13 +922,17 @@ test.options += --no-progress error: unknown package libfoo-1.2.0.tar.gz EOE - $* -d cfg2 libfoo/1.0.0 +{ --config-id 1 } 2>>~%EOE% != 0; + test.arguments = $regex.apply($test.arguments, cfg, cfg2); + + $* libfoo/1.0.0 +{ --config-id 1 } 2>>~%EOE% != 0; %error: unable to downgrade package libfoo/1.1.0 \[cfg.\] to 1.0.0% % info: because package libbar \[cfg.\] depends on \(libfoo == 1.1.0\)% info: explicitly request up/downgrade of package libbar info: or explicitly specify package libfoo version to manually satisfy these constraints EOE + test.arguments = $regex.apply($test.arguments, cfg2, cfg); + $* libfoo/1.1.0 --keep-unused >'update libfoo/1.1.0'; $pkg_disfigure libbar 2>'disfigured libbar/1.1.0'; @@ -4333,15 +4337,6 @@ else error: no configuration with uuid 18f48b4b-b5d9-4712-b98c-1930df1c4228 is linked with cfg/ EOE } - - : multiple - : - { - $clone_cfg; - $* libbaz --config-id 1 --config-name foo 2>>/EOE != 0 - error: multiple --config-* specified - EOE - } } : baz @@ -4410,7 +4405,9 @@ else $cfg_link -d cfg cfg-bar; $cfg_link -d cfg-bar cfg-foo; - $* libbar@"$rep/t4b" -d cfg-bar ?libfoo +{ --config-id 2 } --trust-yes 2>>~%EOE%; + test.arguments = $regex.apply($test.arguments, cfg, cfg-bar); + + $* libbar@"$rep/t4b" ?libfoo +{ --config-id 2 } --trust-yes 2>>~%EOE%; added pkg:build2.org/pkg-build/t4b fetching pkg:build2.org/pkg-build/t4b fetching pkg:build2.org/pkg-build/t4a (prerequisite of pkg:build2.org/pkg-build/t4b) @@ -4426,6 +4423,8 @@ else updated libbar/1.1.0 EOE + test.arguments = $regex.apply($test.arguments, cfg-bar, cfg); + $* libfoo --config-uuid $uuid 2>>~%EOE%; %info: cfg-foo.+libfoo-1.1.0.+ is up to date% %updated libfoo/1.1.0 \[cfg-foo.\]% @@ -4647,7 +4646,11 @@ else $rep_add -d cfg3 $rep/t7a && $rep_fetch -d cfg3; - $* -d cfg2 libbaz +{ --config-id 2 } 2>!; + test.arguments = $regex.apply($test.arguments, cfg, cfg2); + + $* libbaz +{ --config-id 2 } 2>!; + + test.arguments = $regex.apply($test.arguments, cfg2, cfg); $* libbar --yes &cfg2/.bpkg/build2/*** 2>>~%EOE%; %fetched libbuild2-bar/1.0.0 \[cfg2..bpkg.build2.\]% @@ -4836,13 +4839,20 @@ else $cfg_create -d cfg3 &cfg3/***; $rep_add -d cfg3 $rep/t7a && $rep_fetch -d cfg3; - $* -d cfg2 --yes libbar 2>!; - $* -d cfg3 --yes libbox 2>!; + test.arguments = $regex.apply($test.arguments, cfg, cfg2); + + $* --yes libbar 2>!; + + test.arguments = $regex.apply($test.arguments, cfg2, cfg3); + + $* --yes libbox 2>!; $clone_cfg; $cfg_link -d cfg cfg2; $cfg_link -d cfg cfg3; + test.arguments = $regex.apply($test.arguments, cfg3, cfg); + $* libfix --yes 2>>~%EOE% != 0 error: package libbaz indirectly required by libfix/1.0.0 is configured in multiple configurations % info: libbaz/1.0.0 \[cfg3.\]% @@ -4859,13 +4869,20 @@ else $cfg_create -d cfg3 &cfg3/***; $rep_add -d cfg3 $rep/t7b && $rep_fetch -d cfg3; - $* -d cfg2 --yes libbar 2>!; - $* -d cfg3 --yes libbox 2>!; + test.arguments = $regex.apply($test.arguments, cfg, cfg2); + + $* --yes libbar 2>!; + + test.arguments = $regex.apply($test.arguments, cfg2, cfg3); + + $* --yes libbox 2>!; $clone_cfg; $cfg_link -d cfg cfg2; $cfg_link -d cfg cfg3; + test.arguments = $regex.apply($test.arguments, cfg3, cfg); + $* libfix --yes 2>>~%EOE%; fetched libfax/1.0.0 unpacked libfax/1.0.0 @@ -4894,7 +4911,9 @@ else $rep_add -d t1 $rep/t7a && $rep_fetch -d t1; - $* -d t1 libbaz --yes 2>!; + test.arguments = $regex.apply($test.arguments, cfg, t1); + + $* libbaz --yes 2>!; $pkg_status -d t1 -r >>/EOO; !libbaz configured 1.0.0 @@ -4902,7 +4921,7 @@ else $rep_add -d t2 $rep/t7a && $rep_fetch -d t2; - $* -d t1 libbaz +{ --config-name t2 } 2>>~%EOE%; + $* libbaz +{ --config-name t2 } 2>>~%EOE%; %fetched libbaz/1.0.0 \[t2.\]% %unpacked libbaz/1.0.0 \[t2.\]% %configured libbaz/1.0.0 \[t2.\]% @@ -4926,13 +4945,15 @@ else $rep_add -d t1 $rep/t7a && $rep_fetch -d t1; - $* -d t1 libbaz --yes 2>!; + test.arguments = $regex.apply($test.arguments, cfg, t1); + + $* libbaz --yes 2>!; $pkg_status -d t1 -r >>/EOO; !libbaz configured 1.0.0 EOO - $* -d t1 ?libbaz +{ --config-name t2 }; + $* ?libbaz +{ --config-name t2 }; $pkg_status -d t1 -r >>/EOO !libbaz configured 1.0.0 @@ -4949,7 +4970,9 @@ else $rep_add -d t1 $rep/t7a && $rep_fetch -d t1; - $* -d t1 libbaz --yes 2>!; + test.arguments = $regex.apply($test.arguments, cfg, t1); + + $* libbaz --yes 2>!; $pkg_status -d t1 -r >>/EOO; !libbaz configured 1.0.0 @@ -4957,7 +4980,7 @@ else $rep_add -d t2 $rep/t7a && $rep_fetch -d t2; - $* -d t1 foo libbaz +{ --config-name t2 } <>~%EOE%; + $* foo libbaz +{ --config-name t2 } <>~%EOE%; y EOI % new libbuild2-bar/1.0.0 \[t1..bpkg.build2.\] \(required by foo\)% @@ -4997,13 +5020,15 @@ else $rep_add -d t1 $rep/t7a && $rep_fetch -d t1; - $* -d t1 libbaz --yes 2>!; + test.arguments = $regex.apply($test.arguments, cfg, t1); + + $* libbaz --yes 2>!; $pkg_status -d t1 -r >>/EOO; !libbaz configured 1.0.0 EOO - $* -d t1 foo ?libbaz +{ --config-name t2 } <>~%EOE%; + $* foo ?libbaz +{ --config-name t2 } <>~%EOE%; y EOI % new libbuild2-bar/1.0.0 \[t1..bpkg.build2.\] \(required by foo\)% @@ -5049,7 +5074,9 @@ else $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 } <>~%EOE%; + test.arguments = $regex.apply($test.arguments, cfg, t1); + + $* libbar ?foo +{ --config-name h1 } <>~%EOE%; y EOI % new libbuild2-bar/1.0.0 \[h1..bpkg.build2.\] \(required by foo \[h1.\]\)% @@ -5086,7 +5113,9 @@ else libbaz configured 1.0.0 EOO - $* -d t2 libbox ?foo +{ --config-name h1 } <>~%EOE%; + test.arguments = $regex.apply($test.arguments, t1, t2); + + $* libbox ?foo +{ --config-name h1 } <>~%EOE%; y EOI % update foo/1.0.0 \[h1.\]% @@ -5112,7 +5141,9 @@ else libbaz configured 1.0.0 EOO - $* -d t1 ?foo +{ --config-name h2 } <>~%EOE%; + test.arguments = $regex.apply($test.arguments, t2, t1); + + $* ?foo +{ --config-name h2 } <>~%EOE%; y y EOI @@ -5164,7 +5195,9 @@ else $rep_add -d t1 $rep/t7a && $rep_fetch -d t1; - $* -d t1 foo --yes 2>!; + test.arguments = $regex.apply($test.arguments, cfg, t1); + + $* foo --yes 2>!; $pkg_status -d t1 -r >>/EOO; !foo configured 1.0.0 @@ -5174,7 +5207,7 @@ else $rep_add -d t2 $rep/t7a && $rep_fetch -d t2; - $* -d t1 libbaz +{ --config-name t2 } <>~%EOE%; + $* libbaz +{ --config-name t2 } <>~%EOE%; y y EOI @@ -5212,7 +5245,9 @@ else $rep_add -d t1 $rep/t7a && $rep_fetch -d t1; - $* -d t1 foo --yes 2>!; + test.arguments = $regex.apply($test.arguments, cfg, t1); + + $* foo --yes 2>!; $pkg_status -d t1 -r >>/EOO; !foo configured 1.0.0 @@ -5222,7 +5257,7 @@ else $rep_add -d t2 $rep/t7a && $rep_fetch -d t2; - $* -d t1 ?foo libbaz +{ --config-name t2 } <>~%EOE%; + $* ?foo libbaz +{ --config-name t2 } <>~%EOE%; y EOI % new libbaz/1.0.0 \[t2.\]% @@ -5257,7 +5292,9 @@ else $rep_add -d h1 $rep/t7a && $rep_fetch -d h1; - $* -d h1 foo --yes 2>!; + test.arguments = $regex.apply($test.arguments, cfg, h1); + + $* foo --yes 2>!; $pkg_status -d h1 -r >>/EOO; !foo configured 1.0.0 @@ -5267,7 +5304,7 @@ else $rep_add -d h2 $rep/t7a && $rep_fetch -d h2; - $* -d h1 libbar libbaz +{ --config-name h2 } <>~%EOE%; + $* libbar libbaz +{ --config-name h2 } <>~%EOE%; y EOI % new libbaz/1.0.0 \[h2.\]% @@ -5313,7 +5350,9 @@ else $rep_add -d h1 $rep/t7a && $rep_fetch -d h1; - $* -d h1 foo --yes 2>!; + test.arguments = $regex.apply($test.arguments, cfg, h1); + + $* foo --yes 2>!; $pkg_status -d h1 -r >>/EOO; !foo configured 1.0.0 @@ -5321,7 +5360,7 @@ else libbuild2-bar [h1/.bpkg/build2/] configured 1.0.0 EOO - $* -d h1 libbar '?sys:foo/1.2.0' ?libbaz +{ --config-name h2 } <>~%EOE%; + $* libbar '?sys:foo/1.2.0' ?libbaz +{ --config-name h2 } <>~%EOE%; y EOI % new libbaz/1.0.0 \[h2.\]% @@ -5354,7 +5393,7 @@ else libbaz [h2/] configured 1.0.0 EOO - $* -d h1 ?foo ?libbaz <>~%EOE%; + $* ?foo ?libbaz <>~%EOE%; y y EOI @@ -5404,14 +5443,16 @@ else $rep_add -d h1 $rep/t7b && $rep_fetch -d h1; - $* -d h1 foo --yes 2>!; + test.arguments = $regex.apply($test.arguments, cfg, h1); + + $* foo --yes 2>!; $rep_remove -d h1 $rep/t7b; $rep_add -d h1 $rep/t7a && $rep_fetch -d h1; $rep_add -d h2 $rep/t7a && $rep_fetch -d h2; - $* -d h1 libbaz +{ --config-name h2 } 2>>EOE != 0 + $* 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 @@ -5428,7 +5469,9 @@ else $rep_add -d h1 $rep/t7a && $rep_fetch -d h1; - $* -d h1 foo --yes 2>!; + test.arguments = $regex.apply($test.arguments, cfg, h1); + + $* foo --yes 2>!; $pkg_status -d h1 -r >>/EOO; !foo configured 1.0.0 @@ -5438,7 +5481,7 @@ else $rep_add -d h2 $rep/t7a && $rep_fetch -d h2; - $* -d h1 libbar ?foo libbaz +{ --config-name h2 } <>~%EOE%; + $* libbar ?foo libbaz +{ --config-name h2 } <>~%EOE%; y EOI % new libbaz/1.0.0 \[h2.\]% @@ -5482,11 +5525,16 @@ else $cfg_link -d h1 h2; $rep_add -d h2 $rep/t7b && $rep_fetch -d h2; - $* -d h2 foo --yes 2>!; + + test.arguments = $regex.apply($test.arguments, cfg, h2); + + $* foo --yes 2>!; $rep_add -d h1 $rep/t7a && $rep_fetch -d h1; - $* -d h1 foo ?libbaz <>~%EOE%; + test.arguments = $regex.apply($test.arguments, h2, h1); + + $* foo ?libbaz <>~%EOE%; y EOI % new libbuild2-bar/1.0.0 \[h1..bpkg.build2.\] \(required by foo\)% @@ -5515,14 +5563,14 @@ else libbaz [h2/] configured 1.1.0 EOO - $* -d h1 ?libbaz/1.0.0 +{ --config-name h2 } 2>>~%EOE% != 0; + $* ?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 } <>~%EOE%; + $* ?libbaz +{ --config-name h2 } <>~%EOE%; y n EOI @@ -5553,7 +5601,9 @@ else $rep_add -d t1 $rep/t7a && $rep_fetch -d t1; - $* -d t1 libbar --yes 2>!; + test.arguments = $regex.apply($test.arguments, cfg, t1); + + $* libbar --yes 2>!; $pkg_status -d t1 -r >>/EOO; !libbar configured 1.0.0 @@ -5569,7 +5619,7 @@ else $rep_add -d t1 $rep/t7b && $rep_fetch -d t1; - $* -d t1 libbar ?foo +{ --config-name h1 } <>~%EOE%; + $* libbar ?foo +{ --config-name h1 } <>~%EOE%; y EOI % new libbaz/1.1.0 \[h1.\] \(required by foo \[h1.\]\)% @@ -5618,7 +5668,9 @@ else $rep_add -d h1 $rep/t7a && $rep_fetch -d h1; - $* -d h1 libbar --yes 2>!; + test.arguments = $regex.apply($test.arguments, cfg, h1); + + $* libbar --yes 2>!; $pkg_status -d h1 -r >>/EOO; !libbar configured 1.0.0 @@ -5630,7 +5682,7 @@ else $rep_add -d h1 $rep/t7b && $rep_fetch -d h1; - $* -d h1 libbar ?foo ?libbaz +{ --config-name h2 } <>~%EOE%; + $* libbar ?foo ?libbaz +{ --config-name h2 } <>~%EOE%; y y EOI @@ -5677,7 +5729,9 @@ else $rep_add -d t1 $rep/t7a && $rep_fetch -d t1; - $* -d t1 libbar --yes 2>!; + test.arguments = $regex.apply($test.arguments, cfg, t1); + + $* libbar --yes 2>!; $pkg_status -d t1 -r >>/EOO; !libbar configured 1.0.0 @@ -5691,13 +5745,17 @@ else $rep_add -d t2 $rep/t7a && $rep_fetch -d t2; - $* -d t2 libbaz --yes 2>!; + test.arguments = $regex.apply($test.arguments, t1, t2); + + $* libbaz --yes 2>!; $cfg_link -d t1 t2 2>!; $rep_add -d t2 $rep/t7b && $rep_fetch -d t2; - $* -d t1 libbaz +{ --config-name t2 } <>~%EOE%; + test.arguments = $regex.apply($test.arguments, t2, t1); + + $* libbaz +{ --config-name t2 } <>~%EOE%; y y EOI @@ -5735,7 +5793,9 @@ else $rep_add -d t1 $rep/t7a && $rep_fetch -d t1; - $* -d t1 libbar --yes 2>!; + test.arguments = $regex.apply($test.arguments, cfg, t1); + + $* libbar --yes 2>!; $pkg_status -d t1 -r >>/EOO; !libbar configured 1.0.0 @@ -5751,7 +5811,7 @@ else $rep_add -d t2 $rep/t7b && $rep_fetch -d t2; - $* -d t1 libbar +{ --config-name t2 } <>~%EOE%; + $* libbar +{ --config-name t2 } <>~%EOE%; y EOI % new libbaz/1.1.0 \[t2..bpkg.host.\] \(required by foo \[t2..bpkg.host.\]\)% @@ -5800,7 +5860,9 @@ else $rep_add -d t1 $rep/t7a && $rep_fetch -d t1; - $* -d t1 libfix --yes 2>!; + test.arguments = $regex.apply($test.arguments, cfg, t1); + + $* libfix --yes 2>!; $pkg_status -d t1 -r >>/EOO; !libfix configured 1.0.0 @@ -5821,7 +5883,7 @@ else $rep_add -d t2 $rep/t7b && $rep_fetch -d t2; $rep_add -d h1 $rep/t7b && $rep_fetch -d h1; - $* -d t1 libfix libfax +{ --config-name t2 } foo +{ --config-name h1 } <>~%EOE%; + $* libfix libfax +{ --config-name t2 } foo +{ --config-name h1 } <>~%EOE%; y y EOI @@ -6035,4 +6097,245 @@ else EOE } } + + : multiple-configs + : + { + cfg_uuid = '18f48b4b-b5d9-4712-b98c-1930df1c4228' + cfg2_uuid = '28f48b4b-b5d9-4712-b98c-1930df1c4228' + cfg3_uuid = '38f48b4b-b5d9-4712-b98c-1930df1c4228' + cfg4_uuid = '48f48b4b-b5d9-4712-b98c-1930df1c4228' + cfg5_uuid = '58f48b4b-b5d9-4712-b98c-1930df1c4228' + + : 2-current-configs + : + { + $cfg_create -d cfg --uuid $cfg_uuid &cfg/***; + $cfg_create -d cfg2 --uuid $cfg2_uuid &cfg2/***; + $cfg_create -d cfg3 --uuid $cfg3_uuid --type build2 &cfg3/***; + + $cfg_link -d cfg cfg3; + $cfg_link -d cfg2 cfg3; + + $rep_add $rep/t7a && $rep_fetch; + $rep_add -d cfg2 $rep/t7a && $rep_fetch -d cfg2; + + test.arguments += -d cfg2; # Now refers 2 current dirs: cfg/ and cfg2/. + + # While at it, make sure --config-uuid is only allowed for multiple + # current configurations. + # + $* foo +{ --config-id 1 } 2>>EOE != 0; + error: --config-id specified for multiple current configurations + info: use --config-uuid to specify configurations in this mode + info: while validating options for foo + EOE + + $* foo +{ --config-name cfg2 } 2>>EOE != 0; + error: --config-name specified for multiple current configurations + info: use --config-uuid to specify configurations in this mode + info: while validating options for foo + EOE + + # While at it, make sure a package must have the configuration + # specified. + # + $* foo 2>>EOE != 0; + error: no configuration specified for foo + info: configuration must be explicitly specified for each package in multi-configurations mode + info: use --config-uuid to specify its configuration + EOE + + # Build foo in cfg/ and cfg2/ with its libbaz dependency colocated and + # libbuild2-bar dependency shared in cfg3/. + # + $* foo +{ --config-uuid $cfg_uuid --config-uuid $cfg2_uuid } <>~%EOE%; + y + EOI + % new libbaz/1.0.0 \[cfg.\] \(required by foo \[cfg.\]\)% + % new libbuild2-bar/1.0.0 \[cfg3.\] \(required by (foo \[cfg2.\], foo \[cfg.\]\)|foo \[cfg.\], foo \[cfg2.\]\))% + % new foo/1.0.0 \[cfg.\]% + % new libbaz/1.0.0 \[cfg2.\] \(required by foo \[cfg2.\]\)% + % new foo/1.0.0 \[cfg2.\]% + %continue\? \[Y/n\] fetched libbaz/1.0.0 \[cfg.\]% + %unpacked libbaz/1.0.0 \[cfg.\]% + %fetched libbuild2-bar/1.0.0 \[cfg3.\]% + %unpacked libbuild2-bar/1.0.0 \[cfg3.\]% + %fetched foo/1.0.0 \[cfg.\]% + %unpacked foo/1.0.0 \[cfg.\]% + %fetched libbaz/1.0.0 \[cfg2.\]% + %unpacked libbaz/1.0.0 \[cfg2.\]% + %fetched foo/1.0.0 \[cfg2.\]% + %unpacked foo/1.0.0 \[cfg2.\]% + %configured libbaz/1.0.0 \[cfg.\]% + %configured libbuild2-bar/1.0.0 \[cfg3.\]% + %configured foo/1.0.0 \[cfg.\]% + %configured libbaz/1.0.0 \[cfg2.\]% + %configured foo/1.0.0 \[cfg2.\]% + %info: cfg.+foo-1.0.0.+ is up to date% + %info: cfg2.+foo-1.0.0.+ is up to date% + %updated foo/1.0.0 \[cfg.\]% + %updated foo/1.0.0 \[cfg2.\]% + EOE + + $pkg_status -d cfg -r >>/EOO; + !foo configured 1.0.0 + libbaz configured 1.0.0 + libbuild2-bar [cfg3/] configured 1.0.0 + EOO + + $pkg_status -d cfg2 -r >>/EOO; + !foo configured 1.0.0 + libbaz configured 1.0.0 + libbuild2-bar [cfg3/] configured 1.0.0 + EOO + + # Move libbuild2-bar to cfg4/ and libbaz to cfg5/. + # + $cfg_create -d cfg4 --uuid $cfg4_uuid --type build2 &cfg4/***; + $cfg_create -d cfg5 --uuid $cfg5_uuid &cfg5/***; + + $cfg_link -d cfg cfg4; + $cfg_link -d cfg2 cfg4; + $cfg_link -d cfg cfg5; + $cfg_link -d cfg2 cfg5; + + $* ?libbuild2-bar +{ --config-uuid $cfg4_uuid } \ + ?libbaz +{ --config-uuid $cfg5_uuid } <>~%EOE%; + y + y + EOI + % new libbuild2-bar/1.0.0 \[cfg4.\]% + % new libbaz/1.0.0 \[cfg5.\]% + % drop libbaz/1.0.0 \[cfg2.\] \(unused\)% + % drop libbuild2-bar/1.0.0 \[cfg3.\] \(unused\)% + % reconfigure foo/1.0.0 \[cfg2.\] \(dependent of libbaz \[cfg5.\], libbuild2-bar \[cfg4.\]\)% + % drop libbaz/1.0.0 \[cfg.\] \(unused\)% + % reconfigure foo/1.0.0 \[cfg.\] \(dependent of libbaz \[cfg5.\], libbuild2-bar \[cfg4.\]\)% + %continue\? \[Y/n\] update dependent packages\? \[Y/n\] disfigured foo/1.0.0 \[cfg.\]% + %disfigured libbaz/1.0.0 \[cfg.\]% + %disfigured foo/1.0.0 \[cfg2.\]% + %disfigured libbuild2-bar/1.0.0 \[cfg3.\]% + %disfigured libbaz/1.0.0 \[cfg2.\]% + %fetched libbuild2-bar/1.0.0 \[cfg4.\]% + %unpacked libbuild2-bar/1.0.0 \[cfg4.\]% + %fetched libbaz/1.0.0 \[cfg5.\]% + %unpacked libbaz/1.0.0 \[cfg5.\]% + %purged libbaz/1.0.0 \[cfg2.\]% + %purged libbuild2-bar/1.0.0 \[cfg3.\]% + %purged libbaz/1.0.0 \[cfg.\]% + %configured libbuild2-bar/1.0.0 \[cfg4.\]% + %configured libbaz/1.0.0 \[cfg5.\]% + %configured foo/1.0.0 \[cfg2.\]% + %configured foo/1.0.0 \[cfg.\]% + %info: cfg4.+libbuild2-bar-1.0.0.+is up to date% + %info: cfg5.+libbaz-1.0.0.+ is up to date% + %info: cfg2.+foo-1.0.0.+ is up to date% + %info: cfg.+foo-1.0.0.+ is up to date% + %updated libbuild2-bar/1.0.0 \[cfg4.\]% + %updated libbaz/1.0.0 \[cfg5.\]% + %updated foo/1.0.0 \[cfg2.\]% + %updated foo/1.0.0 \[cfg.\]% + EOE + + $pkg_status -d cfg -r >>/EOO; + !foo configured 1.0.0 + libbaz [cfg5/] configured 1.0.0 + libbuild2-bar [cfg4/] configured 1.0.0 + EOO + + $pkg_status -d cfg2 -r >>/EOO; + !foo configured 1.0.0 + libbaz [cfg5/] configured 1.0.0 + libbuild2-bar [cfg4/] configured 1.0.0 + EOO + + $rep_add $rep/t7b && $rep_fetch; + + $* foo ?libbaz --config-uuid $cfg_uuid <>~%EOE%; + y + EOI + % new libbaz/1.1.0 \[cfg.\]% + % upgrade foo/1.1.0 \[cfg.\]% + %continue\? \[Y/n\] disfigured foo/1.0.0 \[cfg.\]% + %fetched libbaz/1.1.0 \[cfg.\]% + %unpacked libbaz/1.1.0 \[cfg.\]% + %fetched foo/1.1.0 \[cfg.\]% + %unpacked foo/1.1.0 \[cfg.\]% + %configured libbaz/1.1.0 \[cfg.\]% + %configured foo/1.1.0 \[cfg.\]% + %info: cfg.+libbaz-1.1.0.+ is up to date% + %info: cfg.+foo-1.1.0.+ is up to date% + %updated libbaz/1.1.0 \[cfg.\]% + %updated foo/1.1.0 \[cfg.\]% + EOE + + $pkg_status -d cfg -r >>/EOO; + !foo configured 1.1.0 + libbaz configured 1.1.0 + EOO + + $pkg_status -d cfg2 -r >>/EOO; + !foo configured 1.0.0 + libbaz [cfg5/] configured 1.0.0 + libbuild2-bar [cfg4/] configured 1.0.0 + EOO + + $rep_add -d cfg2 $rep/t7b && $rep_fetch -d cfg2; + + $* --no-move foo +{ --config-uuid $cfg2_uuid } \ + ?libbaz +{ --config-uuid $cfg5_uuid } <>~%EOE%; + y + EOI + % upgrade libbaz/1.1.0 \[cfg5.\]% + % drop libbuild2-bar/1.0.0 \[cfg4.\] \(unused\)% + % upgrade foo/1.1.0 \[cfg2.\]% + %continue\? \[Y/n\] disfigured foo/1.0.0 \[cfg2.\]% + %disfigured libbuild2-bar/1.0.0 \[cfg4.\]% + %disfigured libbaz/1.0.0 \[cfg5.\]% + %fetched libbaz/1.1.0 \[cfg5.\]% + %unpacked libbaz/1.1.0 \[cfg5.\]% + %purged libbuild2-bar/1.0.0 \[cfg4.\]% + %fetched foo/1.1.0 \[cfg2.\]% + %unpacked foo/1.1.0 \[cfg2.\]% + %configured libbaz/1.1.0 \[cfg5.\]% + %configured foo/1.1.0 \[cfg2.\]% + %info: cfg5.+libbaz-1.1.0.+ is up to date% + %info: cfg2.+foo-1.1.0.+ is up to date% + %updated libbaz/1.1.0 \[cfg5.\]% + %updated foo/1.1.0 \[cfg2.\]% + EOE + + $pkg_status -d cfg2 -r >>/EOO + !foo configured 1.1.0 + libbaz [cfg5/] configured 1.1.0 + EOO + } + + : variable + : + { + $cfg_create -d cfg --uuid $cfg_uuid &cfg/***; + $cfg_create -d cfg2 --uuid $cfg2_uuid &cfg2/***; + $cfg_create -d cfg3 --uuid $cfg3_uuid --type host &cfg3/***; + + $cfg_link -d cfg cfg3; + $cfg_link -d cfg2 cfg3; + + $rep_add $rep/t7a && $rep_fetch; + $rep_add -d cfg2 $rep/t7a && $rep_fetch -d cfg2; + + test.arguments += -d cfg2; # Now refers 2 current dirs: cfg/ and cfg2/. + + $* --configure-only \ + { --config-uuid $cfg_uuid config.libbaz=true }+ libbaz \ + { --config-uuid $cfg2_uuid }+ libbaz 2>!; + + sed -n -e 's/^config.libbaz = (.+)$/\1/p' \ + cfg/libbaz-1.0.0/build/config.build >'true'; + + sed -n -e 's/^config.libbaz = (.+)$/\1/p' \ + cfg2/libbaz-1.0.0/build/config.build >'false' + } + } } diff --git a/tests/pkg-drop.testscript b/tests/pkg-drop.testscript index 7a93c2d..c9b629b 100644 --- a/tests/pkg-drop.testscript +++ b/tests/pkg-drop.testscript @@ -460,7 +460,12 @@ $* libfoo/1.0.0 2>>~%EOE% != 0 cp -pr ../cfg-bar ./; cp -pr ../cfg-foo ./; - $pkg_build libbar@"$rep/t4b" -d cfg-bar ?libfoo +{ --config-id 2 } --trust-yes; + pkg_build = $regex.apply($pkg_build, cfg, cfg-bar); + + $pkg_build libbar@"$rep/t4b" ?libfoo +{ --config-id 2 } --trust-yes; + + pkg_build = $regex.apply($pkg_build, cfg-bar, cfg); + $pkg_build libbaz; $pkg_build '?libbar' +{ --config-id 1 }; @@ -491,7 +496,12 @@ $* libfoo/1.0.0 2>>~%EOE% != 0 cp -pr ../cfg-bar ./; cp -pr ../cfg-foo ./; - $pkg_build libbar@"$rep/t4b" -d cfg-bar ?libfoo +{ --config-id 2 } --trust-yes; + pkg_build = $regex.apply($pkg_build, cfg, cfg-bar); + + $pkg_build libbar@"$rep/t4b" ?libfoo +{ --config-id 2 } --trust-yes; + + pkg_build = $regex.apply($pkg_build, cfg-bar, cfg); + $pkg_build libbaz; # Make sure that dependents of a package being dropped can be found in @@ -549,7 +559,12 @@ $* libfoo/1.0.0 2>>~%EOE% != 0 $cfg_link -d cfg-bar cfg; $cfg_link -d cfg-foo cfg-bar; - $pkg_build libbar@"$rep/t4b" -d cfg-bar ?libfoo +{ --config-id 2 } --trust-yes; + pkg_build = $regex.apply($pkg_build, cfg, cfg-bar); + + $pkg_build libbar@"$rep/t4b" ?libfoo +{ --config-id 2 } --trust-yes; + + pkg_build = $regex.apply($pkg_build, cfg-bar, cfg); + $pkg_build libbaz; $pkg_status -r libbaz >>/EOO; -- cgit v1.1