aboutsummaryrefslogtreecommitdiff
path: root/bpkg/pkg-build.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'bpkg/pkg-build.cxx')
-rw-r--r--bpkg/pkg-build.cxx186
1 files changed, 161 insertions, 25 deletions
diff --git a/bpkg/pkg-build.cxx b/bpkg/pkg-build.cxx
index fac79c2..b8917ca 100644
--- a/bpkg/pkg-build.cxx
+++ b/bpkg/pkg-build.cxx
@@ -2097,6 +2097,8 @@ namespace bpkg
// Specifically, try to find the best available package version considering
// all the imposed constraints as per unsatisfied_dependents description. If
// succeed, return the command line adjustment reflecting the replacement.
+ // If allow_downgrade is false, then don't return a downgrade adjustment for
+ // the package, unless it is being deorphaned.
//
// Notes:
//
@@ -2133,9 +2135,10 @@ namespace bpkg
// requested to be upgraded, patched, and/or deorphaned, then we
// shouldn't be silently up/down-grading it.
//
- optional<cmdline_adjustment>
+ static optional<cmdline_adjustment>
try_replace_dependency (const common_options& o,
const build_package& p,
+ bool allow_downgrade,
const build_packages& pkgs,
const vector<build_package>& hold_pkgs,
const dependency_packages& dep_pkgs,
@@ -2423,7 +2426,7 @@ namespace bpkg
{
r = false;
- if (c.dependent.version && !c.selected_dependent)
+ if (c.dependent.version && !c.existing_dependent)
{
package_key pk (c.dependent.db, c.dependent.name);
@@ -2473,18 +2476,21 @@ namespace bpkg
// then the selected one, then what we currently have is the best that
// we can get. Thus, we use the selected version as a replacement,
// unless it doesn't satisfy all the constraints or we are deorphaning.
+ // Bail out if we cannot stay with the selected version and downgrade is
+ // not allowed.
//
if (constraint == nullptr && sp != nullptr)
{
const version& sv (sp->version);
- if (av < sv && !sp->system () && !p.deorphan)
+ if (av < sv && !p.deorphan)
{
- // Only consider the selected package if its version is satisfactory
- // for its new dependents (note: must be checked first since has a
- // byproduct), differs from the version being replaced, and was
- // never used for the same command line (see above for details).
+ // Only consider to keep the selected non-system package if its
+ // version is satisfactory for its new dependents (note: must be
+ // checked first since has a byproduct), differs from the version
+ // being replaced, and was never used for the same command line (see
+ // above for details).
//
- if (satisfactory (sv) && sv != ver)
+ if (!sp->system () && satisfactory (sv) && sv != ver)
{
if (!cmdline_adjs.tried_earlier (db, nm, sv))
{
@@ -2493,9 +2499,17 @@ namespace bpkg
}
else
l5 ([&]{trace << "selected package replacement "
- << package_version_key (db, nm, sp->version)
- << " tried earlier for same command line, "
- << "skipping";});
+ << package_version_key (db, nm, sv) << " tried "
+ << "earlier for same command line, skipping";});
+ }
+
+ if (!allow_downgrade)
+ {
+ l5 ([&]{trace << "downgrade for "
+ << package_version_key (db, nm, sv) << " is not "
+ << "allowed, bailing out";});
+
+ break;
}
}
}
@@ -2693,11 +2707,12 @@ namespace bpkg
// of the specified dependency with a different available version,
// satisfactory for all its new and existing dependents (if any). Return the
// command line adjustment if such a replacement is deduced and nullopt
- // otherwise. It is assumed that the dependency replacement has been
- // (unsuccessfully) tried by using the try_replace_dependency() call and its
- // resulting list of the dependents, unsatisfied by some of the dependency
- // available versions, is also passed to the function call as the
- // unsatisfied_dpts argument.
+ // otherwise. If allow_downgrade is false, then don't return a downgrade
+ // adjustment, except for a being deorphaned dependent. It is assumed that
+ // the dependency replacement has been (unsuccessfully) tried by using the
+ // try_replace_dependency() call and its resulting list of the dependents,
+ // unsatisfied by some of the dependency available versions, is also passed
+ // to the function call as the unsatisfied_dpts argument.
//
// Specifically, try to replace the dependents in the following order by
// calling try_replace_dependency() for them:
@@ -2723,9 +2738,10 @@ namespace bpkg
// - Dependents of all the above types of dependents, discovered by
// recursively calling try_replace_dependent() for them.
//
- optional<cmdline_adjustment>
+ static optional<cmdline_adjustment>
try_replace_dependent (const common_options& o,
const build_package& p, // Dependency.
+ bool allow_downgrade,
const vector<unsatisfied_constraint>* ucs,
const build_packages& pkgs,
const cmdline_adjustments& cmdline_adjs,
@@ -2758,6 +2774,7 @@ namespace bpkg
//
auto try_replace = [&o,
&p,
+ allow_downgrade,
&pkgs,
&cmdline_adjs,
&hold_pkgs,
@@ -2793,6 +2810,7 @@ namespace bpkg
if (optional<cmdline_adjustment> a = try_replace_dependency (
o,
*d,
+ allow_downgrade,
pkgs,
hold_pkgs,
dep_pkgs,
@@ -2816,7 +2834,7 @@ namespace bpkg
{
const package_version_key& dvk (c.dependent);
- if (dvk.version && !c.selected_dependent && !satisfies (av, c.value))
+ if (dvk.version && !c.existing_dependent && !satisfies (av, c.value))
{
if (optional<cmdline_adjustment> a = try_replace (
package_key (dvk.db, dvk.name), "unsatisfied dependent"))
@@ -2851,7 +2869,7 @@ namespace bpkg
{
const package_version_key& dvk (c1.dependent);
- if (dvk.version && !c1.selected_dependent)
+ if (dvk.version && !c1.existing_dependent)
{
const version_constraint& v1 (c1.value);
@@ -2900,6 +2918,7 @@ namespace bpkg
if (optional<cmdline_adjustment> a = try_replace_dependent (
o,
*d,
+ allow_downgrade,
nullptr /* unsatisfied_constraints */,
pkgs,
cmdline_adjs,
@@ -4298,6 +4317,13 @@ namespace bpkg
optional<cmdline_adjustment> cmdline_refine_adjustment;
optional<size_t> cmdline_refine_index;
+ // If an --upgrade* or --patch* option is used on the command line, then
+ // we will try to avoid any package downgrades, which may potentially end
+ // up with noop. However, we will still falls back to allowing such
+ // downgrades on the resolution failure.
+ //
+ bool cmdline_adjs_allow_downgrade (true);
+
{
// Check if the package is a duplicate. Return true if it is but
// harmless.
@@ -4843,9 +4869,14 @@ namespace bpkg
// configuration.
//
if (pdb != nullptr)
+ {
+ if (u)
+ cmdline_adjs_allow_downgrade = false;
+
rec_pkgs.push_back (recursive_package {*pdb, pa.name,
r, u && *u,
d});
+ }
}
}
@@ -4901,6 +4932,10 @@ namespace bpkg
bool hold_version (pa.constraint.has_value ());
+ optional<bool> upgrade (pa.options.upgrade () || pa.options.patch ()
+ ? pa.options.upgrade ()
+ : optional<bool> ());
+
dep_pkgs.push_back (
dependency_package {pdb,
move (pa.name),
@@ -4909,9 +4944,7 @@ namespace bpkg
move (sp),
sys,
existing,
- (pa.options.upgrade () || pa.options.patch ()
- ? pa.options.upgrade ()
- : optional<bool> ()),
+ upgrade,
pa.options.deorphan (),
pa.options.keep_out (),
pa.options.disfigure (),
@@ -4921,6 +4954,10 @@ namespace bpkg
pa.options.checkout_purge (),
move (pa.config_vars),
pa.system_status});
+
+ if (upgrade)
+ cmdline_adjs_allow_downgrade = false;
+
continue;
}
@@ -5154,6 +5191,9 @@ namespace bpkg
pkg_confs.emplace_back (p.db, p.name ());
+ if (p.upgrade)
+ cmdline_adjs_allow_downgrade = false;
+
hold_pkgs.push_back (move (p));
}
@@ -5279,6 +5319,9 @@ namespace bpkg
l4 ([&]{trace << "stash held package "
<< p.available_name_version_db ();});
+ if (p.upgrade)
+ cmdline_adjs_allow_downgrade = false;
+
hold_pkgs.push_back (move (p));
// If there are also -i|-r, then we are also upgrading and/or
@@ -6761,7 +6804,7 @@ namespace bpkg
// so we need to bring it back.
//
// Make sure that selected packages are only owned by the session
- // and the build package list.
+ // and the build package (pkgs) and the dependency (dep_pkgs) lists.
//
build_pkgs.clear ();
@@ -6871,12 +6914,83 @@ namespace bpkg
//
if (!rescan)
{
+ // Return true if the specified package is loaded as a
+ // prerequisite of some dependent package, cached in the
+ // session, and contained in a different database. In this case
+ // unload this package from all such dependents.
+ //
+ auto check_unload_prereq = [&ses, &sp_session]
+ (const shared_ptr<selected_package>& sp,
+ const odb::database* db)
+ {
+ bool r (false);
+
+ for (const auto& dps: ses.map ())
+ {
+ // Skip dependents from the same database.
+ //
+ if (dps.first == db)
+ continue;
+
+ if (const selected_packages* sps = sp_session (dps.second))
+ {
+ for (const auto& p: *sps)
+ {
+ for (auto& pr: p.second->prerequisites)
+ {
+ const lazy_shared_ptr<selected_package>& lp (pr.first);
+
+ if (lp.loaded () && lp.get_eager () == sp)
+ {
+ lp.unload ();
+ r = true;
+ }
+ }
+ }
+ }
+ }
+
+ return r;
+ };
+
for (const auto& dps: ses.map ())
{
if (const selected_packages* sps = sp_session (dps.second))
{
if (old_sp.find (dps.first) == old_sp.end ())
- assert (sps->empty ());
+ {
+ // Note that the valid reason for these packages to still
+ // be present in the session is that some of them may be
+ // referenced as prerequisites by some dependent packages
+ // from other databases and reference the remaining
+ // packages. For example:
+ //
+ // new session: A (X, 2) -> B (X, 2) -> C (Y, 2) -> D (Y, 2)
+ // old session: A
+ //
+ // Here C and D are the packages in question, package A is
+ // present in both sessions, X and Y are the databases,
+ // the numbers are the package reference counts, and the
+ // arrows denote the loaded prerequisite lazy pointers.
+ //
+ // Let's verify that's the only situation by unloading
+ // these packages from such dependent prerequisites and
+ // rescanning.
+ //
+ if (!sps->empty ())
+ {
+ for (const auto& p: *sps)
+ {
+ if (check_unload_prereq (p.second, dps.first))
+ rescan = true;
+ }
+
+ // If we didn't unload any of these packages, then we
+ // consider this a bug.
+ //
+ assert (rescan);
+ }
+ }
}
}
}
@@ -6951,6 +7065,7 @@ namespace bpkg
if ((a = try_replace_dependency (o,
*p,
+ cmdline_adjs_allow_downgrade,
pkgs,
hold_pkgs,
dep_pkgs,
@@ -6959,6 +7074,7 @@ namespace bpkg
"unsatisfactory dependency")) ||
(a = try_replace_dependent (o,
*p,
+ cmdline_adjs_allow_downgrade,
&ic.unsatisfied_constraints,
pkgs,
cmdline_adjs,
@@ -6984,7 +7100,27 @@ namespace bpkg
prepare_recollect ();
}
else
- unsatisfied_depts.diag (pkgs); // Issue the diagnostics and fail.
+ {
+ // If we fail to resolve the unsatisfied dependency
+ // constraints with the downgrades disallowed, then allow
+ // downgrades and retry from the very beginning.
+ //
+ if (!cmdline_adjs_allow_downgrade)
+ {
+ l5 ([&]{trace << "cannot resolve unsatisfied dependency "
+ << "constraints, now allowing downgrades";});
+
+ cmdline_adjs_allow_downgrade = true;
+
+ prepare_recollect ();
+ }
+ else
+ {
+ // Issue the diagnostics and fail.
+ //
+ unsatisfied_depts.diag (pkgs);
+ }
+ }
}
else // We are in the command line adjustments refinement cycle.
{