From dfb2b32071be8003c9048128cc8cf52bf2137d30 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Wed, 1 Dec 2021 16:16:17 +0300 Subject: Improve pkg-build diagnostics issued when unable to satisfy dependency constraint --- bpkg/pkg-build.cxx | 81 ++++++++++++++++++++++++++++++++++++++-------- tests/pkg-build.testscript | 9 +++--- 2 files changed, 73 insertions(+), 17 deletions(-) diff --git a/bpkg/pkg-build.cxx b/bpkg/pkg-build.cxx index c0374ac..d027866 100644 --- a/bpkg/pkg-build.cxx +++ b/bpkg/pkg-build.cxx @@ -182,9 +182,8 @@ namespace bpkg // Note that we return (loaded) lazy_shared_ptr in order to also convey // the database to which it belongs. // - static - vector, - lazy_shared_ptr>> + static vector, + lazy_shared_ptr>> find_available (const linked_databases& dbs, const package_name& name, const optional& c) @@ -232,9 +231,8 @@ namespace bpkg // fragments, their prerequisite repositories, and their complements, // recursively (note: recursivity applies to complements, not prerequisites). // - static - vector, - lazy_shared_ptr>> + static vector, + lazy_shared_ptr>> find_available (const package_name& name, const optional& c, const config_repo_fragments& rfs, @@ -268,6 +266,35 @@ namespace bpkg return r; } + // As above but only look for packages from a single repository fragment, + // its prerequisite repositories, and its complements, recursively (note: + // recursivity applies to complements, not prerequisites). Doesn't provide + // the repository fragments the packages come from. + // + // It is assumed that the repository fragment lazy pointer contains the + // database information. + // + static vector> + find_available (const package_name& name, + const optional& c, + const lazy_shared_ptr& rf, + bool prereq = true) + { + vector> r; + + database& db (rf.database ()); + for (auto& ap: filter (rf.load (), query_available (db, name, c), prereq)) + r.emplace_back (move (ap)); + + if (r.empty ()) + { + if (shared_ptr ap = find_imaginary_stub (name)) + r.emplace_back (move (ap)); + } + + return r; + } + // As above but only look for a single package from the specified repository // fragment, its prerequisite repositories, and their complements, // recursively (note: recursivity applies to complements, not @@ -1534,17 +1561,45 @@ namespace bpkg } diag_record dr (fail); - dr << "unknown dependency " << dn; - // We need to be careful not to print the wildcard-based - // constraint. + // Issue diagnostics differently based on the presence of + // available packages for the unsatisfied dependency. // - if (d.constraint && (!dep_constr || !wildcard (*dep_constr))) - dr << ' ' << *d.constraint; + // Note that there can't be any stubs, since they satisfy any + // constraint and we won't be here if they were. + // + vector> aps ( + find_available (dn, nullopt /* version_constraint */, af)); + + if (!aps.empty ()) + { + dr << "unable to satisfy dependency constraint (" << dn; - dr << " of package " << name << pdb; + // We need to be careful not to print the wildcard-based + // constraint. + // + if (d.constraint && (!dep_constr || !wildcard (*dep_constr))) + dr << ' ' << *d.constraint; - if (!af->location.empty () && (!dep_constr || system)) + dr << ") of package " << name << pdb << + info << "available " << dn << " versions:"; + + for (const shared_ptr& ap: aps) + dr << ' ' << ap->version; + } + else + { + dr << "no package available for dependency " << dn + << " of package " << name << pdb; + } + + // Avoid printing this if the dependent package is external since + // it's more often confusing than helpful (they are normally not + // fetched manually). + // + if (!af->location.empty () && + !af->location.directory_based () && + (!dep_constr || system)) dr << info << "repository " << af->location << " appears to " << "be broken" << info << "or the repository state could be stale" << diff --git a/tests/pkg-build.testscript b/tests/pkg-build.testscript index 48fea7b..058a08b 100644 --- a/tests/pkg-build.testscript +++ b/tests/pkg-build.testscript @@ -431,7 +431,7 @@ test.options += --no-progress : $clone_root_cfg; $* $src/libbar-1.0.0.tar.gz 2>>EOE != 0 - error: unknown dependency libfoo of package libbar + error: no package available for dependency libfoo of package libbar info: while satisfying libbar/1.0.0 EOE @@ -443,7 +443,7 @@ test.options += --no-progress $cfg_create -d cfg2 &cfg2/***; $cfg_link -d cfg cfg2; $* $src/libbar-1.0.0.tar.gz +{ --config-id 1 } 2>>~%EOE% != 0 - %error: unknown dependency libfoo of package libbar \[cfg2.\]% + %error: no package available for dependency libfoo of package libbar \[cfg2.\]% %info: while satisfying libbar/1.0.0 \[cfg2.\]% EOE @@ -1633,7 +1633,7 @@ test.options += --no-progress $rep_fetch; $* libfoo 2>>~%EOE% != 0; - error: unknown dependency libhello >= 1.0 of package libfoo + error: no package available for dependency libhello of package libfoo %.+ EOE @@ -2052,7 +2052,8 @@ test.options += --no-progress $rep_fetch $rep/t0a $rep/t0c; $* libbar/1.0.0 ?libfoo/0.0.1 2>>EOE != 0 - error: unknown dependency libfoo == 0.0.1 of package libbar + error: unable to satisfy dependency constraint (libfoo == 0.0.1) of package libbar + info: available libfoo versions: 1.0.0 info: while satisfying libbar/1.0.0 EOE } -- cgit v1.1