From ad53b2152e10b133165c95f08f218e80f1dd8580 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Wed, 25 Oct 2023 18:14:35 +0300 Subject: Improve pkg-build's 'unable to upgrade package' diagnostics --- bpkg/database.hxx | 5 +- bpkg/package.cxx | 33 ++++++ bpkg/package.hxx | 66 ++++++++++- bpkg/pkg-build-collect.cxx | 283 +++++++++++++++++++++++++++++++------------- bpkg/pkg-build-collect.hxx | 71 ++++++++--- bpkg/pkg-build.cxx | 35 ++++-- bpkg/rep-create.cxx | 12 +- tests/pkg-build.testscript | 65 +++++----- tests/pkg-system.testscript | 2 +- 9 files changed, 417 insertions(+), 155 deletions(-) diff --git a/bpkg/database.hxx b/bpkg/database.hxx index 684dc9d..a9e1da5 100644 --- a/bpkg/database.hxx +++ b/bpkg/database.hxx @@ -504,8 +504,9 @@ namespace bpkg linked_databases implicit_links_; }; - // NOTE: remember to update config_package comparison operators and - // compare_lazy_ptr if changing the database comparison operators. + // NOTE: remember to update package_key and package_version_key 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.cxx b/bpkg/package.cxx index aca3550..4beba8e 100644 --- a/bpkg/package.cxx +++ b/bpkg/package.cxx @@ -70,6 +70,39 @@ namespace bpkg return r != 0 ? (r < 0) : (db < v.db); } + // package_version_key + // + string package_version_key:: + string (bool ignore_version) const + { + std::string r (name.string ()); + + if (version && !version->empty () && !ignore_version) + { + r += '/'; + r += version->string (); + } + + const std::string& d (db.get ().string); + + if (!d.empty ()) + { + r += ' '; + r += d; + } + + return r; + } + + bool package_version_key:: + operator< (const package_version_key& v) const + { + if (int r = name.compare (v.name)) + return r < 0; + + return version != v.version ? (version < v.version) : (db < v.db); + } + // available_package // const version* available_package:: diff --git a/bpkg/package.hxx b/bpkg/package.hxx index 06278cd..8f4b46c 100644 --- a/bpkg/package.hxx +++ b/bpkg/package.hxx @@ -1614,12 +1614,6 @@ namespace bpkg package_key (database& d, package_name n): db (d), name (move (n)) {} - // Create a pseudo-package (command line as a dependent, etc). - // - package_key (database& d, string n) - : db (d), - name (n.empty () ? package_name () : package_name (move (n))) {} - bool operator== (const package_key& v) const { @@ -1651,6 +1645,66 @@ namespace bpkg return os << p.string (); } + // Database, package name, and package version. + // + // It is normally used as a key for maps containing data for package + // versions across multiple linked configurations. Assumes that the + // respective databases are not detached during such map lifetimes. + // Considers all package name, package version, and database for objects + // comparison. + // + // The package name can be a pseudo-package (command line as a dependent, + // etc), in which case the version is absent. The version can also be empty, + // denoting a package of an unknown version. + // + struct package_version_key + { + reference_wrapper db; + package_name name; + optional version; + + package_version_key (database& d, package_name n, bpkg::version v) + : db (d), name (move (n)), version (move (v)) {} + + // Create a pseudo-package (command line as a dependent, etc). + // + package_version_key (database& d, string n) + : db (d), + name (move (n), package_name::raw_string) {} + + bool + operator== (const package_version_key& v) const + { + // See operator==(database, database). + // + return name == v.name && + version == v.version && + &db.get () == &v.db.get (); + } + + bool + operator!= (const package_version_key& v) const + { + return !(*this == v); + } + + bool + operator< (const package_version_key&) const; + + // Return the package string representation in the form: + // + // [/] [ ] + // + std::string + string (bool ignore_version = false) const; + }; + + inline ostream& + operator<< (ostream& os, const package_version_key& p) + { + return os << p.string (); + } + // Return a count of repositories that contain this repository fragment. // #pragma db view table("main.repository_fragments") diff --git a/bpkg/pkg-build-collect.cxx b/bpkg/pkg-build-collect.cxx index 918242c..b1306b5 100644 --- a/bpkg/pkg-build-collect.cxx +++ b/bpkg/pkg-build-collect.cxx @@ -62,7 +62,8 @@ namespace bpkg bool build_package:: user_selection () const { - return required_by.find (package_key {db.get ().main_database (), ""}) != + return required_by.find (package_version_key (db.get ().main_database (), + "command line")) != required_by.end (); } @@ -303,7 +304,7 @@ namespace bpkg // Propagate the user-selection tag. // - required_by.emplace (db.get ().main_database (), ""); + required_by.emplace (db.get ().main_database (), "command line"); } // Copy the required-by package names only if semantics matches. @@ -320,10 +321,7 @@ namespace bpkg if (find_if (constraints.begin (), constraints.end (), [&c] (const constraint_type& v) { - return v.db.get () == c.db.get () && - v.dependent == c.dependent && - v.value == c.value; - + return v.dependent == c.dependent && v.value == c.value; }) == constraints.end ()) { constraints.push_back (move (c)); @@ -525,8 +523,8 @@ namespace bpkg // int ud (sp->version.compare (av)); - // Otherwise, the dependent must be satisfied with the already configured - // dependency. + // Otherwise, the dependent must be satisfied with the already + // configured dependency. // assert (ud != 0); @@ -542,24 +540,81 @@ namespace bpkg else dr << av; // Can't be the wildcard otherwise would satisfy. - dr << info << "because package " << dk << " depends on (" << n << ' ' - << c << ')'; + shared_ptr dsp ( + dk.db.get ().load (dk.name)); + + assert (dsp != nullptr); // By definition. + dr << info << "because configured package " << *dsp << dk.db + << " depends on (" << n << ' ' << c << ')'; + + // Print the dependency constraints tree for this unsatisfied dependent, + // which only contains constraints which come from its selected + // dependents, recursively. + // { set printed; - pkgs.print_constraints (dr, dk, indent, printed); + pkgs.print_constraints (dr, + dk, + indent, + printed, + true /* selected_dependent */); } - string rb; + // If the dependency we failed to up/downgrade is not explicitly + // specified on the command line, then print its dependency constraints + // tree which only contains constraints which come from its being built + // dependents, recursively. + // if (!p->user_selection ()) { - for (const package_key& pk: p->required_by) - rb += (rb.empty () ? " " : ", ") + pk.string (); - } + // The dependency upgrade is always required by someone, the command + // line or a package. + // + assert (!p->required_by.empty ()); + + dr << info << "package " << p->available_name_version_db (); + + // Note that if the required_by member contains the dependencies, + // rather than the dependents, we will subsequently print the + // dependency constraints trees for these dependencies rather than a + // single constraints tree (rooted in the dependency we failed to + // up/downgrade). Also note that in this case we will still reuse the + // same printed packages cache for all print_constraints() calls, + // since it will likely be considered as a single dependency graph by + // the user. + // + bool rbd (p->required_by_dependents); + dr << (rbd ? " required by" : " dependent of"); - if (!rb.empty ()) - dr << info << "package " << p->available_name_version () - << " required by" << rb; + set printed; + for (const package_version_key& pvk: p->required_by) + { + dr << '\n' << indent << pvk; + + if (rbd) + { + const vector& cs (p->constraints); + auto i (find_if (cs.begin (), cs.end (), + [&pvk] (const build_package::constraint_type& v) + { + return v.dependent == pvk; + })); + + if (i != cs.end ()) + dr << " (" << p->name () << ' ' << i->value << ')'; + } + + indent += " "; + pkgs.print_constraints (dr, + package_key (pvk.db, pvk.name), + indent, + printed, + false /* selected_dependent */); + + indent.resize (indent.size () - 2); + } + } dr << info << "consider re-trying with --upgrade|-u potentially combined " << "with --recursive|-r" << @@ -576,8 +631,8 @@ namespace bpkg { const build_package::constraint_type& c (uc.constraint); - dr << info << c.dependent << c.db << " depends on (" << n - << ' ' << c.value << ")"; + dr << info << c.dependent << " depends on (" << n << ' ' << c.value + << ')'; if (const build_package* d = pkgs.dependent_build (c)) { @@ -1268,17 +1323,16 @@ namespace bpkg dependent_build (const build_package::constraint_type& c) const { const build_package* r (nullptr); - const string& d (c.dependent); - if (d != "command line") + if (c.dependent.version) try { - r = entered_build (c.db, package_name (d)); + r = entered_build (c.dependent.db, c.dependent.name); assert (r != nullptr); // Expected to be collected. } catch (const invalid_argument&) { - // Must be a package name, unless it is 'command line'. + // Must be a package name since the version is specified. // assert (false); } @@ -1943,7 +1997,8 @@ namespace bpkg replaced_vers, postponed_recs, postponed_cfgs, - unsatisfied_depts); + unsatisfied_depts, + true /* add_required_by */); } // Postpone the recursive collection of a dependency if the existing @@ -2473,15 +2528,15 @@ namespace bpkg *dr << error << "unable to satisfy constraints on package " << dn << info << nm << pdb << " depends on (" << dn << ' ' - << *dp.constraint << ")"; + << *dp.constraint << ')'; { set printed; print_constraints (*dr, pkg, indent, printed); } - *dr << info << c.dependent << c.db << " depends on (" << dn - << ' ' << c.value << ")"; + *dr << info << c.dependent << " depends on (" << dn << ' ' + << c.value << ')'; if (const build_package* d = dependent_build (c)) { @@ -2725,7 +2780,7 @@ namespace bpkg assert (dr == nullptr); // Should fail on the "silent" run. fail << "multiple possible " << type << " configurations " - << "for build-time dependency (" << dp << ")" << + << "for build-time dependency (" << dp << ')' << info << db->config_orig << info << ldb.config_orig << info << "use --config-* to select the configuration"; @@ -3106,7 +3161,7 @@ namespace bpkg info << package_string (dn, *dap->system_version (*ddb), true /* system */) - << " does not satisfy the constrains"; + << " does not satisfy the constrains"; return precollect_result (false /* postpone */); } @@ -3181,7 +3236,11 @@ namespace bpkg { using constraint_type = build_package::constraint_type; - constraint_type c1 {pdb, nm.string (), *d.constraint}; + constraint_type c1 (*d.constraint, + pdb, + nm, + pkg.available_version (), + false /* selected_dependent */); if (!satisfies (v2, c1.value)) { @@ -3204,8 +3263,8 @@ namespace bpkg *dr << error << "unable to satisfy constraints on " << "package " << n << - info << c2.dependent << c2.db << " depends on (" - << n << ' ' << c2.value << ")"; + info << c2.dependent << " depends on (" << n + << ' ' << c2.value << ')'; if (const build_package* d = dependent_build (c2)) { @@ -3213,9 +3272,8 @@ namespace bpkg print_constraints (*dr, *d, indent, printed); } - *dr << info << c1.dependent << c1.db - << " depends on (" << n << ' ' - << c1.value << ")"; + *dr << info << c1.dependent << " depends on (" + << n << ' ' << c1.value << ')'; if (const build_package* d = dependent_build (c1)) { @@ -3308,6 +3366,8 @@ namespace bpkg postponed_deps.erase (d); })); + package_version_key pvk (pk.db, pk.name, pkg.available_version ()); + for (prebuild& b: bs) { build_package bpk { @@ -3331,7 +3391,7 @@ namespace bpkg nullopt, // Checkout root. false, // Checkout purge. strings (), // Configuration variables. - {pk}, // Required by (dependent). + {pvk}, // Required by (dependent). true, // Required by dependents. 0}; // State flags. @@ -3346,7 +3406,11 @@ namespace bpkg // both constraints for completeness. // if (constraint) - bpk.constraints.emplace_back (pdb, nm.string (), *constraint); + bpk.constraints.emplace_back (*constraint, + pdb, + nm, + pkg.available_version (), + false /* selected_dependent */); // Now collect this prerequisite. If it was actually collected // (i.e., it wasn't already there) and we are forcing a downgrade @@ -5073,13 +5137,19 @@ namespace bpkg // Add the prerequisite replacements as the required-by packages. // - set required_by; + set required_by; for (const auto& prq: rd.second) { if (prq.second) // Prerequisite replacement? { const package_key& pk (prq.first); - required_by.emplace (pk.db, pk.name); + + // Note that the dependency can potentially be just pre-entered, in + // which case its version is not known at this point. + // + assert (entered_build (pk) != nullptr); + + required_by.emplace (pk.db, pk.name, version ()); } } @@ -5630,7 +5700,8 @@ namespace bpkg replaced_vers, postponed_recs, postponed_cfgs, - unsatisfied_depts); + unsatisfied_depts, + true /* add_required_by */); } } } @@ -6515,12 +6586,18 @@ namespace bpkg << "existing dependent " << *ed.selected << ed.db;}); + // Note that we pass false as the add_required_by argument since + // the package builds collection state has been restored and the + // originating dependency for this existing dependent may not be + // collected anymore. + // recollect_existing_dependent (o, ed, replaced_vers, postponed_recs, postponed_cfgs, - unsatisfied_depts); + unsatisfied_depts, + false /* add_required_by */); } } } @@ -6691,12 +6768,17 @@ namespace bpkg << ed.db << " due to bogus postponement of " << "dependency " << pk;}); + // Note that we pass false as the add_required_by argument since + // the postponment is bogus and thus the originating dependency + // for this existing dependent may not be collected. + // recollect_existing_dependent (o, ed, replaced_vers, postponed_recs, postponed_cfgs, - unsatisfied_depts); + unsatisfied_depts, + false /* add_required_by */); prog = true; break; } @@ -7072,6 +7154,8 @@ namespace bpkg // assert (!dsp->system ()); + package_version_key pvk (pdb, n, version ()); + return build_package { build_package::adjust, ddb, @@ -7093,7 +7177,7 @@ namespace bpkg nullopt, // Checkout root. false, // Checkout purge. strings (), // Configuration variables. - {package_key {pdb, n}}, // Required by (dependency). + {move (pvk)}, // Required by (dependency). false, // Required by dependents. build_package::adjust_reconfigure}; }; @@ -7160,11 +7244,29 @@ namespace bpkg i->second.position = insert (pos, i->second.package); } - // Add this dependent's contraint, if present, to the dependency's - // constraints list for completeness. + // Add this dependent's constraint, if present, to the dependency's + // constraints list for completeness, while suppressing duplicates. // if (dc) - p.constraints.emplace_back (ddb, move (dn).string (), move (*dc)); + { + using constraint_type = build_package::constraint_type; + + constraint_type c (move (*dc), + ddb, + move (dn), + i->second.package.selected->version, + true /* selected_dependent */); + + if (find_if (p.constraints.begin (), p.constraints.end (), + [&c] (const constraint_type& v) + { + return v.dependent == c.dependent && + v.value == c.value; + }) == p.constraints.end ()) + { + p.constraints.emplace_back (move (c)); + } + } // Recursively collect our own dependents inserting them before us. // @@ -7200,7 +7302,8 @@ namespace bpkg print_constraints (diag_record& dr, const build_package& p, string& indent, - set& printed) const + set& printed, + optional selected_dependent) const { using constraint_type = build_package::constraint_type; @@ -7216,33 +7319,35 @@ namespace bpkg for (const constraint_type& c: cs) { - if (const build_package* d = dependent_build (c)) + if (!selected_dependent || + *selected_dependent == c.selected_dependent) { - dr << '\n' << indent << c.dependent << '/'; + if (const build_package* d = dependent_build (c)) + { + dr << '\n' << indent << c.dependent << " requires (" << pk + << ' ' << c.value << ')'; - // The dependent can only be collected as a build or adjustment. - // - assert (d->action && - d->action != build_package::drop && - (d->available || d->selected)); - - dr << (d->available != nullptr - ? d->available_version () - : d->selected->version) << c.db << " requires (" << pk - << ' ' << c.value << ")"; - - indent += " "; - print_constraints (dr, *d, indent, printed); - indent.resize (indent.size () - 2); + indent += " "; + print_constraints (dr, *d, indent, printed, selected_dependent); + indent.resize (indent.size () - 2); + } + else + dr << '\n' << indent << c.dependent << " requires (" << pk << ' ' + << c.value << ')'; } - else - dr << '\n' << indent << c.dependent << " requires (" << pk << ' ' - << c.value << ")"; } } else { - dr << '\n' << indent << "..."; + for (const constraint_type& c: cs) + { + if (!selected_dependent || + *selected_dependent == c.selected_dependent) + { + dr << '\n' << indent << "..."; + break; + } + } } } } @@ -7251,11 +7356,12 @@ namespace bpkg print_constraints (diag_record& dr, const package_key& pk, string& indent, - set& printed) const + set& printed, + optional selected_dependent) const { const build_package* p (entered_build (pk)); assert (p != nullptr); // Expected to be collected. - print_constraints (dr, *p, indent, printed); + print_constraints (dr, *p, indent, printed, selected_dependent); } void build_packages:: @@ -7570,8 +7676,8 @@ namespace bpkg const shared_ptr& dsp (ed.selected); - package_key dpt (ed.db, dsp->name); - const package_key& dep (*ed.dependency); + package_version_key dpt (ed.db, dsp->name, dsp->version); + const package_key& dep (*ed.dependency); lazy_shared_ptr lsp (dep.db.get (), dep.name); shared_ptr sp (lsp.load ()); @@ -7614,9 +7720,11 @@ namespace bpkg assert (i != dsp->prerequisites.end ()); if (i->second.constraint) - p.constraints.emplace_back (dpt.db, - dpt.name.string (), - *i->second.constraint); + p.constraints.emplace_back (*i->second.constraint, + dpt.db, + dpt.name, + *dpt.version, + true /* selected_package */); } // Note: not recursive. @@ -7641,6 +7749,11 @@ namespace bpkg lazy_shared_ptr> rp ( find_available_fragment (o, ed.db, ed.selected)); + set rb; + + for (package_key& p: ds) + rb.emplace (p.db, move (p.name), version ()); + build_package p { build_package::build, ed.db, @@ -7662,9 +7775,7 @@ namespace bpkg nullopt, // Checkout root. false, // Checkout purge. strings (), // Configuration variables. - set ( // Required by (dependency). - make_move_iterator (ds.begin ()), - make_move_iterator (ds.end ())), + move (rb), // Required by (dependency). false, // Required by dependents. build_package::build_reevaluate}; @@ -7680,7 +7791,8 @@ namespace bpkg replaced_versions& replaced_vers, postponed_packages& postponed_recs, postponed_configurations& postponed_cfgs, - unsatisfied_dependents& unsatisfied_depts) + unsatisfied_dependents& unsatisfied_depts, + bool add_required_by) { pair, lazy_shared_ptr> rp ( @@ -7693,6 +7805,17 @@ namespace bpkg if (!ed.dependency) flags |= build_package::adjust_reconfigure; + set rb; + + if (add_required_by) + { + const package_key& pk (ed.originating_dependency); + + assert (entered_build (pk) != nullptr); // Expected to be collected. + + rb.emplace (pk.db, pk.name, version ()); + } + build_package p { build_package::build, ed.db, @@ -7714,7 +7837,7 @@ namespace bpkg nullopt, // Checkout root. false, // Checkout purge. strings (), // Configuration variables. - {ed.originating_dependency}, // Required by (dependency). + move (rb), // Required by (dependency). false, // Required by dependents. flags}; diff --git a/bpkg/pkg-build-collect.hxx b/bpkg/pkg-build-collect.hxx index 5a5a397..e34fc25 100644 --- a/bpkg/pkg-build-collect.hxx +++ b/bpkg/pkg-build-collect.hxx @@ -196,24 +196,43 @@ namespace bpkg optional hold_package; optional hold_version; - // Constraint value plus, normally, the dependent package name that placed - // this constraint but can also be some other name for the initial - // selection. This is why we use the string type, rather than - // package_name. Currently, the only valid non-package name is "command - // line", which is used when the package version is specified by the user - // on the command line. + // Constraint value plus, normally, the dependent package name/version + // that placed this constraint but can also be some other name (in which + // case the version is absent) for the initial selection. Currently, the + // only valid non-package name is 'command line', which is used when the + // package version is constrained by the user on the command line. // // Note that if the dependent is a package name, then this package is // expected to be collected (present in the map). // struct constraint_type { - reference_wrapper db; // Main database for non-packages. - string dependent; version_constraint value; - constraint_type (database& d, string dp, version_constraint v) - : db (d), dependent (move (dp)), value (move (v)) {} + package_version_key dependent; + + // False for non-packages. Otherwise, indicates whether the constraint + // comes from the selected dependent or not. + // + bool selected_dependent; + + // Create constraint for a package dependent. + // + constraint_type (version_constraint v, + database& db, + package_name nm, + version ver, + bool s) + : value (move (v)), + dependent (db, move (nm), move (ver)), + selected_dependent (s) {} + + // Create constraint for a non-package dependent. + // + constraint_type (version_constraint v, database& db, string nm) + : value (move (v)), + dependent (db, move (nm)), + selected_dependent (false) {} }; vector constraints; @@ -273,11 +292,23 @@ namespace bpkg strings config_vars; // Set of packages (dependents or dependencies but not a mix) 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. + // this package to be built or adjusted. The 'command line' name signifies + // user selection and can be present regardless of the + // required_by_dependents flag value. // - std::set required_by; + // Note that if this is a package name, then this package is expected to + // be collected (present in the map), potentially just pre-entered if + // required_by_dependents is false. + // + // Also note that if required_by_dependents is true, then all the + // dependent package versions in the required_by set are expected to be + // known (the version members are not empty). Otherwise (the required_by + // set contains dependencies), since it's not always easy to deduce the + // dependency versions at the time of collecting the dependent build (see + // collect_repointed_dependents() implementation for details), the + // dependency package versions are expected to all be unknown. + // + std::set required_by; // If this flags is true, then required_by contains dependents. // @@ -1583,17 +1614,22 @@ namespace bpkg // constraints for the same package twice, printing "..." instead. Noop if // there are no constraints for this package. // + // Optionally, only print constraints from the selected or being built + // dependents (see build_package::constraint_type for details). + // void print_constraints (diag_record&, const build_package&, string& indent, - std::set& printed) const; + std::set& printed, + optional selected_dependent = nullopt) const; void print_constraints (diag_record&, const package_key&, string& indent, - std::set& printed) const; + std::set& printed, + optional selected_dependent = nullopt) const; // Verify that builds ordering is consistent across all the data // structures and the ordering expectations are fulfilled (real build @@ -1698,7 +1734,8 @@ namespace bpkg replaced_versions&, postponed_packages& postponed_recs, postponed_configurations&, - unsatisfied_dependents&); + unsatisfied_dependents&, + bool add_required_by); struct package_ref { diff --git a/bpkg/pkg-build.cxx b/bpkg/pkg-build.cxx index 588d29c..5c41204 100644 --- a/bpkg/pkg-build.cxx +++ b/bpkg/pkg-build.cxx @@ -1660,6 +1660,10 @@ namespace bpkg ? empty_string : '[' + config_dirs[0].representation () + ']')); + // Command line as a dependent. + // + package_version_key cmd_line (mdb, "command line"); + current_configs.push_back (mdb); if (config_dirs.size () != 1) @@ -3609,7 +3613,7 @@ namespace bpkg : optional ()), pa.options.checkout_purge (), move (pa.config_vars), - {package_key {mdb, ""}}, // Required by (command line). + {cmd_line}, // Required by (command line). false, // Required by dependents. replace ? build_package::build_replace : uint16_t (0)}; @@ -3623,7 +3627,7 @@ namespace bpkg // if (pa.constraint) p.constraints.emplace_back ( - mdb, "command line", move (*pa.constraint)); + move (*pa.constraint), cmd_line.db, cmd_line.name.string ()); pkg_confs.emplace_back (p.db, p.name ()); @@ -3741,7 +3745,7 @@ namespace bpkg nullopt, // Checkout root. false, // Checkout purge. strings (), // Configuration variables. - {package_key {mdb, ""}}, // Required by (command line). + {cmd_line}, // Required by (command line). false, // Required by dependents. deorphan ? build_package::build_replace : uint16_t (0)}; @@ -4082,7 +4086,8 @@ namespace bpkg // Also, if a dependency package already has selected package that // is held, then we need to unhold it. // - auto enter = [&mdb, &pkgs] (database& db, const dependency_package& p) + auto enter = [&pkgs, &cmd_line] (database& db, + const dependency_package& p) { build_package bp { nullopt, // Action. @@ -4105,13 +4110,14 @@ namespace bpkg p.checkout_root, p.checkout_purge, p.config_vars, - {package_key {mdb, ""}}, // Required by (command line). + {cmd_line}, // Required by (command line). false, // Required by dependents. 0}; // State flags. if (p.constraint) - bp.constraints.emplace_back ( - mdb, "command line", *p.constraint); + bp.constraints.emplace_back (*p.constraint, + cmd_line.db, + cmd_line.name.string ()); pkgs.enter (p.name, move (bp)); }; @@ -4403,7 +4409,7 @@ namespace bpkg nullopt, // Checkout root. false, // Checkout purge. strings (), // Configuration variables. - {package_key {mdb, ""}}, // Required by (command line). + {cmd_line}, // Required by (command line). false, // Required by dependents. (d.existing || d.deorphan ? build_package::build_replace @@ -5600,12 +5606,17 @@ namespace bpkg // can a dependent-dependency structure change without any of // the package versions changing? Doesn't feel like it should. // - for (const package_key& pk: p.required_by) + for (const package_version_key& pvk: p.required_by) { - // Skip the command-line dependent. + // Skip the command-line, etc dependents and don't print the + // package version (which is not always available; see + // build_package::required_by for details). // - if (!pk.name.empty ()) - rb += (rb.empty () ? " " : ", ") + pk.string (); + if (pvk.version) // Is it a real package? + { + rb += (rb.empty () ? " " : ", ") + + pvk.string (true /* ignore_version */); + } } // If not user-selected, then there should be another (implicit) diff --git a/bpkg/rep-create.cxx b/bpkg/rep-create.cxx index 67cca95..9b9bdeb 100644 --- a/bpkg/rep-create.cxx +++ b/bpkg/rep-create.cxx @@ -24,7 +24,7 @@ using namespace butl; namespace bpkg { - struct package_version_key + struct package_name_version { package_name name; bpkg::version version; @@ -34,20 +34,20 @@ namespace bpkg // revision. // bool - operator< (const package_version_key& y) const + operator< (const package_name_version& y) const { int r (name.compare (y.name)); return r < 0 || (r == 0 && version.compare (y.version, true) < 0); } }; - struct package_version_data + struct package_data { path archive; package_manifest manifest; }; - using package_map = map; + using package_map = map; static void collect (const rep_create_options& o, @@ -116,8 +116,8 @@ namespace bpkg // m.location = a.leaf (root); - package_version_key k {m.name, m.version}; // Argument evaluation order. - auto r (map.emplace (move (k), package_version_data {a, move (m)})); + package_name_version k {m.name, m.version}; // Argument evaluation order. + auto r (map.emplace (move (k), package_data {a, move (m)})); // Diagnose duplicates. // diff --git a/tests/pkg-build.testscript b/tests/pkg-build.testscript index 0efe4ff..eec268e 100644 --- a/tests/pkg-build.testscript +++ b/tests/pkg-build.testscript @@ -1392,7 +1392,7 @@ test.arguments += --sys-no-query $* libfoo/1.0.0 2>>EOE != 0; error: unable to downgrade package libfoo/1.1.0 to 1.0.0 - info: because package libbar depends on (libfoo == 1.1.0) + info: because configured package libbar/1.1.0 depends on (libfoo == 1.1.0) info: consider re-trying with --upgrade|-u potentially combined with --recursive|-r info: or explicitly request up/downgrade of package libbar info: or explicitly specify package libfoo version to manually satisfy these constraints @@ -1428,7 +1428,7 @@ test.arguments += --sys-no-query $* 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: because configured package libbar/1.1.0 \[cfg.\] depends on \(libfoo == 1.1.0\)% info: consider re-trying with --upgrade|-u potentially combined with --recursive|-r info: or explicitly request up/downgrade of package libbar info: or explicitly specify package libfoo version to manually satisfy these constraints @@ -2164,8 +2164,8 @@ test.arguments += --sys-no-query # $* foo 2>>EOE != 0; error: unable to satisfy constraints on package libbar - info: libbaz depends on (libbar ^2.0.0) - info: libbox depends on (libbar ^1.0.0) + info: libbaz/2.0.0 depends on (libbar ^2.0.0) + info: libbox/1.0.0 depends on (libbar ^1.0.0) info: available libbar/2.0.0 info: available libbar/1.0.0 info: while satisfying libbox/1.0.0 @@ -2234,8 +2234,9 @@ test.arguments += --sys-no-query # $* ?libbaz 2>>EOE != 0; error: unable to upgrade package libbar/1.0.0 to 2.0.0 - info: because package libbox depends on (libbar ^1.0.0) - info: package libbar/2.0.0 required by libbaz + info: because configured package libbox/1.0.0 depends on (libbar ^1.0.0) + info: package libbar/2.0.0 required by + libbaz/2.0.0 (libbar ^2.0.0) info: consider re-trying with --upgrade|-u potentially combined with --recursive|-r info: or explicitly request up/downgrade of package libbox info: or explicitly specify package libbar version to manually satisfy these constraints @@ -2259,8 +2260,8 @@ test.arguments += --sys-no-query # $* foo 2>>EOE != 0; error: unable to satisfy constraints on package libbar - info: libbaz depends on (libbar ^2.0.0) - info: libbox depends on (libbar ^1.0.0) + info: libbaz/2.0.0 depends on (libbar ^2.0.0) + info: libbox/1.1.0 depends on (libbar ^1.0.0) info: available libbar/2.0.0 info: available libbar/1.0.0 info: while satisfying libbox/1.1.0 @@ -2329,8 +2330,9 @@ test.arguments += --sys-no-query # $* foo ?libbaz 2>>EOE != 0; error: unable to upgrade package libbar/1.0.0 to 2.0.0 - info: because package libbox depends on (libbar ^1.0.0) - info: package libbar/2.0.0 required by libbaz + info: because configured package libbox/1.0.0 depends on (libbar ^1.0.0) + info: package libbar/2.0.0 required by + libbaz/2.0.0 (libbar ^2.0.0) info: consider re-trying with --upgrade|-u potentially combined with --recursive|-r info: or explicitly request up/downgrade of package libbox info: or explicitly specify package libbar version to manually satisfy these constraints @@ -2341,8 +2343,8 @@ test.arguments += --sys-no-query # $* foo ?libbaz ?libbox 2>>EOE != 0; error: unable to satisfy constraints on package libbar - info: libbaz depends on (libbar ^2.0.0) - info: libbox depends on (libbar ^1.0.0) + info: libbaz/2.0.0 depends on (libbar ^2.0.0) + info: libbox/1.1.0 depends on (libbar ^1.0.0) info: available libbar/2.0.0 info: available libbar/1.0.0 info: while satisfying libbox/1.1.0 @@ -2833,8 +2835,8 @@ test.arguments += --sys-no-query $* libfix libbiz 2>>EOE != 0; error: unable to satisfy constraints on package libbaz - info: libfix depends on (libbaz >= 0.0.3) - info: libbiz depends on (libbaz <= 0.0.3) + info: libfix/0.0.3 depends on (libbaz >= 0.0.3) + info: libbiz/0.0.2 depends on (libbaz <= 0.0.3) info: available libbaz/0.1.0 info: available libbaz/0.0.2 info: while satisfying libbiz/0.0.2 @@ -2907,8 +2909,8 @@ test.arguments += --sys-no-query $* libbiz 2>>EOE != 0; error: unable to satisfy constraints on package libfoo - info: libbox depends on (libfoo == 1.0.0) - info: libfox depends on (libfoo == 0.0.1) + info: libbox/0.0.2 depends on (libfoo == 1.0.0) + info: libfox/0.0.2 depends on (libfoo == 0.0.1) info: available libfoo/1.0.0 info: available libfoo/0.0.1 info: while satisfying libbox/0.0.2 @@ -4448,7 +4450,7 @@ test.arguments += --sys-no-query $* box +{ config.box.extras=true } libbox/0.1.0 2>>EOE != 0; error: unable to satisfy constraints on package libbox info: command line depends on (libbox == 0.1.0) - info: box depends on (libbox >= 0.1.1) + info: box/1.0.0 depends on (libbox >= 0.1.1) info: available libbox/0.1.0 info: available libbox/1.0.0 info: while satisfying box/1.0.0 @@ -5815,8 +5817,9 @@ test.arguments += --sys-no-query $* tax 2>>EOE != 0; error: unable to downgrade package libfoo/2.0.0 to 1.0.0 - info: because package tpx depends on (libfoo >= 2.0.0) - info: package libfoo/1.0.0 required by tax + info: because configured package tpx/1.0.0 depends on (libfoo >= 2.0.0) + info: package libfoo/1.0.0 required by + tax/1.0.0 (libfoo == 1.0.0) info: consider re-trying with --upgrade|-u potentially combined with --recursive|-r info: or explicitly request up/downgrade of package tpx info: or explicitly specify package libfoo version to manually satisfy these constraints @@ -5829,7 +5832,7 @@ test.arguments += --sys-no-query # $* tax tpx ?libfoo/1.0.0 2>>EOE != 0; error: unable to downgrade package libfoo/2.0.0 to 1.0.0 - info: because package tpx depends on (libfoo >= 2.0.0) + info: because configured package tpx/1.0.0 depends on (libfoo >= 2.0.0) info: consider re-trying with --upgrade|-u potentially combined with --recursive|-r info: or explicitly request up/downgrade of package tpx info: or explicitly specify package libfoo version to manually satisfy these constraints @@ -9647,8 +9650,8 @@ test.arguments += --sys-no-query trace: execute_plan: while configuring dependent tez/1.0.0 in simulation mode unconstrain (toz == 0.1.0) %.* error: unable to satisfy constraints on package toz - info: tvz depends on (toz == 0.2.0) - info: tez depends on (toz == 0.1.0) + info: tvz/1.0.0 depends on (toz == 0.2.0) + info: tez/1.0.0 depends on (toz == 0.1.0) info: available toz/0.2.0 info: available toz/0.1.0 info: while satisfying tez/1.0.0 @@ -9841,9 +9844,9 @@ test.arguments += --sys-no-query trace: execute_plan: while configuring dependent tez/1.0.0 in simulation mode unconstrain (toz == 0.1.0) %.* error: unable to satisfy constraints on package toz - info: tvz depends on (toz == 0.2.0) + info: tvz/0.1.0 depends on (toz == 0.2.0) command line requires (tvz == 0.1.0) - info: tez depends on (toz == 0.1.0) + info: tez/1.0.0 depends on (toz == 0.1.0) info: available toz/0.2.0 info: available toz/0.1.0 info: while satisfying tez/1.0.0 @@ -16957,8 +16960,8 @@ test.arguments += --sys-no-query # $* tvz --verbose 1 2>>EOE != 0; error: unable to satisfy constraints on package toz - info: tvz depends on (toz == 0.2.0) - info: tez depends on (toz == 0.1.0) + info: tvz/1.0.0 depends on (toz == 0.2.0) + info: tez/1.0.0 depends on (toz == 0.1.0) info: available toz/0.2.0 info: available toz/0.1.0 info: while satisfying tez/1.0.0 @@ -24765,7 +24768,7 @@ else $* ?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: because configured package foo/1.1.0 \[h2.\] depends on \(libbaz \^1.1.0\)% info: consider re-trying with --upgrade|-u potentially combined with --recursive|-r info: or explicitly request up/downgrade of package foo info: or explicitly specify package libbaz version to manually satisfy these constraints @@ -28185,7 +28188,7 @@ else $* libbar $src/libfoo-1.0.0.tar.gz 2>>~%EOE% != 0 error: unable to satisfy constraints on package libfoo info: command line depends on (libfoo == 1.0.0) - info: libbar depends on (libfoo == 1.1.0) + info: libbar/1.1.0 depends on (libfoo == 1.1.0) info: available libfoo/1.0.0 info: available libfoo/1.1.0 info: while satisfying libbar/1.1.0 @@ -28204,7 +28207,7 @@ else $* $src/libbar-1.1.0.tar.gz $src/libfoo-1.0.0.tar.gz 2>>~%EOE% != 0 error: unable to satisfy constraints on package libfoo info: command line depends on (libfoo == 1.0.0) - info: libbar depends on (libfoo == 1.1.0) + info: libbar/1.1.0 depends on (libfoo == 1.1.0) command line requires (libbar == 1.1.0) info: available libfoo/1.0.0 info: available libfoo/1.1.0 @@ -28615,7 +28618,7 @@ else $* libbar $d/libfoo-1.0.0/ 2>>~%EOE% != 0 error: unable to satisfy constraints on package libfoo info: command line depends on (libfoo == 1.0.0) - info: libbar depends on (libfoo == 1.1.0) + info: libbar/1.1.0 depends on (libfoo == 1.1.0) info: available libfoo/1.0.0 info: available libfoo/1.1.0 info: while satisfying libbar/1.1.0 @@ -28634,7 +28637,7 @@ else $* $d/libbar-1.1.0/ $d/libfoo-1.0.0/ 2>>~%EOE% != 0 error: unable to satisfy constraints on package libfoo info: command line depends on (libfoo == 1.0.0) - info: libbar depends on (libfoo == 1.1.0) + info: libbar/1.1.0 depends on (libfoo == 1.1.0) command line requires (libbar == 1.1.0) info: available libfoo/1.0.0 info: available libfoo/1.1.0 diff --git a/tests/pkg-system.testscript b/tests/pkg-system.testscript index 8aaabd9..11ed1cc 100644 --- a/tests/pkg-system.testscript +++ b/tests/pkg-system.testscript @@ -711,7 +711,7 @@ rep_remove += -d cfg 2>! $pkg_build foo 'sys:libbar/1' 2>>EOE != 0; error: unable to satisfy constraints on package libbar info: command line depends on (libbar == 1) - info: foo depends on (libbar >= 2) + info: foo/2 depends on (libbar >= 2) info: available sys:libbar/1 info: available sys:libbar/2 info: while satisfying foo/2 -- cgit v1.1