|
|
@@ -160,11 +160,11 @@ namespace Usm {
|
|
|
public Vector<RebuildEntry> rebuilds { get; private set; default = new Vector<RebuildEntry>(); }
|
|
|
|
|
|
/**
|
|
|
- * Emitted when a Ctrl+C lands mid-transaction: handlers answer
|
|
|
- * whether to keep going (true — the interrupted operation re-runs)
|
|
|
- * or roll the journaled transaction back (false). With no handler
|
|
|
- * connected the signal's default return is false — the rollback
|
|
|
- * behaviour non-interactive contexts want.
|
|
|
+ * Emitted when a Ctrl+C lands mid-transaction: a true return
|
|
|
+ * keeps the transaction going (the interrupted operation
|
|
|
+ * re-runs), false rolls the journaled transaction back. No
|
|
|
+ * handler is connected by the CLI — a Ctrl+C always rolls back
|
|
|
+ * — leaving this as the programmatic opt-out for embedders.
|
|
|
*/
|
|
|
public signal bool interrupt_decided(string subject, string phase_verb);
|
|
|
|
|
|
@@ -499,24 +499,31 @@ namespace Usm {
|
|
|
}
|
|
|
});
|
|
|
// Build-phase refs must be satisfied by resources
|
|
|
- // that SURVIVE the removal phase and are already
|
|
|
- // present or installed by an EARLIER lot: a
|
|
|
- // same-lot provide is not installed yet when this
|
|
|
- // package builds, so a build dependency on it defers
|
|
|
- // the package to the next lot
|
|
|
- PredicateDelegate<ResourceRef> buildtime_satisfied = d => !removed_by_plan(d) && (
|
|
|
- resource_finder.has_resource(d)
|
|
|
- || preinstalled_resources.any(r => d.satisfied_by(r))
|
|
|
- || installed_by_earlier_lots.any(r => d.satisfied_by(r)));
|
|
|
- // Manage-phase executables run at install time, when
|
|
|
- // everything this lot has already admitted (in
|
|
|
- // topological order) is installed — but nothing the
|
|
|
- // removal phase takes away
|
|
|
- PredicateDelegate<ResourceRef> installtime_satisfied = d => !removed_by_plan(d) && (
|
|
|
- resource_finder.has_resource(d)
|
|
|
- || preinstalled_resources.any(r => d.satisfied_by(r))
|
|
|
- || installed_by_earlier_lots.any(r => d.satisfied_by(r))
|
|
|
- || installed_by_this_lot.any(r => d.satisfied_by(r)));
|
|
|
+ // that are in place when this package builds:
|
|
|
+ // either installed by an EARLIER lot (the
|
|
|
+ // replacement of an updated pair — this dominates,
|
|
|
+ // the removal is already undone by then) or
|
|
|
+ // present on the destination and NOT provided by
|
|
|
+ // a package this transaction removes (those go
|
|
|
+ // away before the first build, so the stale
|
|
|
+ // on-disk state satisfies nothing). A same-lot
|
|
|
+ // provide is not installed yet and defers the
|
|
|
+ // package to the next lot.
|
|
|
+ PredicateDelegate<ResourceRef> buildtime_satisfied = d =>
|
|
|
+ installed_by_earlier_lots.any(r => d.satisfied_by(r))
|
|
|
+ || (!removed_by_plan(d) && (
|
|
|
+ resource_finder.has_resource(d)
|
|
|
+ || preinstalled_resources.any(r => d.satisfied_by(r))));
|
|
|
+ // Manage-phase executables run at install time,
|
|
|
+ // when everything this lot has already admitted
|
|
|
+ // (in topological order) is installed — the same
|
|
|
+ // dominance and poison rules as the build phase
|
|
|
+ PredicateDelegate<ResourceRef> installtime_satisfied = d =>
|
|
|
+ installed_by_earlier_lots.any(r => d.satisfied_by(r))
|
|
|
+ || installed_by_this_lot.any(r => d.satisfied_by(r))
|
|
|
+ || (!removed_by_plan(d) && (
|
|
|
+ resource_finder.has_resource(d)
|
|
|
+ || preinstalled_resources.any(r => d.satisfied_by(r))));
|
|
|
if(manifest.dependencies.manage.is_satisfied(installtime_satisfied) &&
|
|
|
manifest.dependencies.build.is_satisfied(buildtime_satisfied)) {
|
|
|
lot.add(package);
|
|
|
@@ -614,9 +621,24 @@ namespace Usm {
|
|
|
private string describe_install_deadlock(Enumerable<CachedPackage> stuck, Set<ResourceRef> installed_by_earlier_lots, Set<CachedPackage> planned_install) {
|
|
|
var stuck_sorted = stuck.sort((a, b) => a.package_name.collate(b.package_name)).to_vector();
|
|
|
|
|
|
- PredicateDelegate<ResourceRef> satisfier = r => resource_finder.has_resource(r)
|
|
|
- || preinstalled_resources.any(p => r.satisfied_by(p))
|
|
|
- || installed_by_earlier_lots.any(p => r.satisfied_by(p));
|
|
|
+ // The same satisfier the scheduler used, poison included: a
|
|
|
+ // resource provided by a package this transaction removes is
|
|
|
+ // going away before the first build, so explaining a stall
|
|
|
+ // against the stale on-disk state would clear every blocker
|
|
|
+ // and produce a dead diagnosis
|
|
|
+ PredicateDelegate<ResourceRef> removed_by_plan = r => planned_removal.any(p => {
|
|
|
+ try {
|
|
|
+ return p.get_manifest().satisfies(r);
|
|
|
+ }
|
|
|
+ catch(Error e) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ PredicateDelegate<ResourceRef> satisfier = r =>
|
|
|
+ installed_by_earlier_lots.any(p => r.satisfied_by(p))
|
|
|
+ || (!removed_by_plan(r) && (
|
|
|
+ resource_finder.has_resource(r)
|
|
|
+ || preinstalled_resources.any(p => r.satisfied_by(p))));
|
|
|
|
|
|
var builder = new StringBuilder();
|
|
|
builder.append_printf("Could not build a transaction strategy: %u package(s) could not be scheduled for installation", stuck_sorted.length);
|
|
|
@@ -634,8 +656,8 @@ namespace Usm {
|
|
|
builder.append_printf(" (manifest could not be read: %s)\n", e.message);
|
|
|
continue;
|
|
|
}
|
|
|
- append_blocking_refs(builder, manifest.dependencies.manage, "manage", satisfier, planned_install, package);
|
|
|
- append_blocking_refs(builder, manifest.dependencies.build, "build", satisfier, planned_install, package);
|
|
|
+ append_blocking_refs(builder, manifest.dependencies.manage, "manage", satisfier, removed_by_plan, planned_install, package);
|
|
|
+ append_blocking_refs(builder, manifest.dependencies.build, "build", satisfier, removed_by_plan, planned_install, package);
|
|
|
}
|
|
|
return builder.str.chomp();
|
|
|
}
|
|
|
@@ -645,14 +667,14 @@ namespace Usm {
|
|
|
* provides. Flat phases list their refs directly; grouped phases
|
|
|
* report every candidate group that is not viable.
|
|
|
*/
|
|
|
- private void append_blocking_refs(StringBuilder builder, DependencyPhase phase, string phase_name, PredicateDelegate<ResourceRef> satisfier, Set<CachedPackage> planned_install, CachedPackage package) {
|
|
|
+ private void append_blocking_refs(StringBuilder builder, DependencyPhase phase, string phase_name, PredicateDelegate<ResourceRef> satisfier, PredicateDelegate<ResourceRef> removed_by_plan, Set<CachedPackage> planned_install, CachedPackage package) {
|
|
|
if(phase == null) {
|
|
|
return;
|
|
|
}
|
|
|
if(!phase.is_grouped) {
|
|
|
foreach(var resource in phase.ordered_required()) {
|
|
|
if(!satisfier(resource)) {
|
|
|
- append_blocking_ref(builder, phase_name, resource, planned_install, package);
|
|
|
+ append_blocking_ref(builder, phase_name, resource, removed_by_plan, planned_install, package);
|
|
|
}
|
|
|
}
|
|
|
return;
|
|
|
@@ -665,7 +687,7 @@ namespace Usm {
|
|
|
}
|
|
|
builder.append_printf(" [%s phase, candidate group %u of %u:]\n", phase_name, index + 1, groups.length);
|
|
|
foreach(var resource in blocking) {
|
|
|
- append_blocking_ref(builder, phase_name, resource, planned_install, package);
|
|
|
+ append_blocking_ref(builder, phase_name, resource, removed_by_plan, planned_install, package);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -675,7 +697,7 @@ namespace Usm {
|
|
|
* transaction members provide it, their names — distinguishing
|
|
|
* unmet dependencies from dependency cycles.
|
|
|
*/
|
|
|
- private void append_blocking_ref(StringBuilder builder, string phase_name, ResourceRef resource, Set<CachedPackage> planned_install, CachedPackage package) {
|
|
|
+ private void append_blocking_ref(StringBuilder builder, string phase_name, ResourceRef resource, PredicateDelegate<ResourceRef> removed_by_plan, Set<CachedPackage> planned_install, CachedPackage package) {
|
|
|
var providers = new Vector<string>();
|
|
|
foreach(var candidate in planned_install) {
|
|
|
if(candidate == package) {
|
|
|
@@ -691,6 +713,13 @@ namespace Usm {
|
|
|
}
|
|
|
}
|
|
|
if(providers.length == 0) {
|
|
|
+ if(removed_by_plan(resource)) {
|
|
|
+ builder.append_printf(
|
|
|
+ " %s (%s phase) is going away: a package this transaction removes currently provides it, and nothing in this transaction replaces it\n",
|
|
|
+ resource.to_string(), phase_name
|
|
|
+ );
|
|
|
+ return;
|
|
|
+ }
|
|
|
builder.append_printf(" %s (%s phase) is unmet: it is not present on this system, no planned system package provides it, and no package in this transaction provides it\n", resource.to_string(), phase_name);
|
|
|
return;
|
|
|
}
|