From aa7d2f55a0b0d683fbcd46d2a80c99957b788c3a Mon Sep 17 00:00:00 2001 From: David Kalnischkies Date: Sat, 25 Apr 2020 10:00:34 +0200 Subject: Discard impossible candidate versions also for non-installed We reseted the candidate for installed packages back to the version which is installed if one of the (critical) dependencies of it is not statisfiable, but we can do the same for non-installed packages by discarding the candidate which beside slightly helping the resolver also improves error messages generated by apt as a sideeffect. --- apt-pkg/depcache.cc | 47 +++++++++++++++++++++-------------------------- 1 file changed, 21 insertions(+), 26 deletions(-) (limited to 'apt-pkg/depcache.cc') diff --git a/apt-pkg/depcache.cc b/apt-pkg/depcache.cc index 052e3de0e..d43c1522e 100644 --- a/apt-pkg/depcache.cc +++ b/apt-pkg/depcache.cc @@ -1399,45 +1399,40 @@ bool pkgDepCache::IsInstallOkDependenciesSatisfiableByCandidates(PkgIterator con for (DepIterator Dep = CandVer.DependsList(); Dep.end() != true;) { - // Grok or groups DepIterator Start = Dep; - bool Result = true; + bool foundSolution = false; unsigned Ors = 0; - for (bool LastOR = true; Dep.end() == false && LastOR == true; ++Dep, ++Ors) + // Is it possible to statisfy this dependency? + for (bool LastOR = true; not Dep.end() && LastOR; ++Dep, ++Ors) { LastOR = (Dep->CompareOp & Dep::Or) == Dep::Or; - if ((DepState[Dep->ID] & DepInstall) == DepInstall) - Result = false; + if ((DepState[Dep->ID] & (DepInstall | DepCVer)) != 0) + foundSolution = true; } - if (Start.IsCritical() == false || Start.IsNegative() == true || Result == false) + if (foundSolution || not Start.IsCritical() || Start.IsNegative()) continue; - /* If we are in an or group locate the first or that can succeed. - We have already cached this… */ - for (; Ors > 1 && (DepState[Start->ID] & DepCVer) != DepCVer; --Ors) - ++Start; + if (DebugAutoInstall == true) + std::clog << OutputInDepth(Depth) << APT::PrettyDep(this, Start) << " can't be satisfied!" << std::endl; - if (Ors == 1 && (DepState[Start->ID] &DepCVer) != DepCVer) + // the dependency is critical, but can't be installed, so discard the candidate + // as the problemresolver will trip over it otherwise trying to install it (#735967) + StateCache &State = PkgState[Pkg->ID]; + if (not State.Protect()) { - if (DebugAutoInstall == true) - std::clog << OutputInDepth(Depth) << APT::PrettyDep(this, Start) << " can't be satisfied!" << std::endl; - - // the dependency is critical, but can't be installed, so discard the candidate - // as the problemresolver will trip over it otherwise trying to install it (#735967) - if (Pkg->CurrentVer != 0 && (PkgState[Pkg->ID].iFlags & Protected) != Protected) - { + if (Pkg->CurrentVer != 0) SetCandidateVersion(Pkg.CurrentVer()); - StateCache &State = PkgState[Pkg->ID]; - if (State.Mode != ModeDelete) - { - State.Mode = ModeKeep; - State.Update(Pkg, *this); - } - } - return false; + else + State.CandidateVer = nullptr; + if (not State.Delete()) + { + State.Mode = ModeKeep; + State.Update(Pkg, *this); + } } + return false; } return true; -- cgit v1.2.3 From ff4555c72df967e40590d9e8c6ce83e9df4c46ea Mon Sep 17 00:00:00 2001 From: David Kalnischkies Date: Sat, 25 Apr 2020 11:28:47 +0200 Subject: Explore or-groups for Recommends further than first MarkInstall only looks at the first alternative in an or-group which has a fighting chance of being satisfiable (= the package itself satisfies the dependency, if it is installable itself is not considered). This is "hidden" for Depends by the problem resolver who will try another member of the or-group later, but Recommends are not a problem for it, so for them the alternatives are never further explored. Exploring the or-group in MarkInstall seems like the better choice for both types as that frees the problem resolver to deal with the hard things like package conflicts. --- apt-pkg/depcache.cc | 65 ++++++++++++++++++++++++++++------------------------- 1 file changed, 35 insertions(+), 30 deletions(-) (limited to 'apt-pkg/depcache.cc') diff --git a/apt-pkg/depcache.cc b/apt-pkg/depcache.cc index d43c1522e..3d510b7e6 100644 --- a/apt-pkg/depcache.cc +++ b/apt-pkg/depcache.cc @@ -1246,42 +1246,47 @@ bool pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst, /* This bit is for processing the possibility of an install/upgrade fixing the problem for "positive" dependencies */ - if (Start.IsNegative() == false && (DepState[Start->ID] & DepCVer) == DepCVer) + if (not Start.IsNegative() && (DepState[Start->ID] & DepCVer) == DepCVer) { - pkgCacheFile CacheFile(this); - APT::VersionList verlist = APT::VersionList::FromDependency(CacheFile, Start, APT::CacheSetHelper::CANDIDATE); - CompareProviders comp(Start); - - do { - APT::VersionList::iterator InstVer = std::max_element(verlist.begin(), verlist.end(), comp); - - if (InstVer == verlist.end()) - break; + bool foundSolution = false; + for (; Start != Dep && not foundSolution; ++Start) + { + pkgCacheFile CacheFile(this); + APT::VersionList verlist = APT::VersionList::FromDependency(CacheFile, Start, APT::CacheSetHelper::CANDIDATE); + CompareProviders comp(Start); - pkgCache::PkgIterator InstPkg = InstVer.ParentPkg(); - if(DebugAutoInstall == true) - std::clog << OutputInDepth(Depth) << "Installing " << InstPkg.Name() - << " as " << Start.DepType() << " of " << Pkg.Name() - << std::endl; - if (MarkInstall(InstPkg, true, Depth + 1, false, ForceImportantDeps) == false) + do { - verlist.erase(InstVer); - continue; - } + APT::VersionList::iterator InstVer = std::max_element(verlist.begin(), verlist.end(), comp); + if (InstVer == verlist.end()) + break; - // now check if we should consider it a automatic dependency or not - if(InstPkg->CurrentVer == 0 && MoveAutoBitToDependencies) - { - if(DebugAutoInstall == true) - std::clog << OutputInDepth(Depth) << "Setting " << InstPkg.FullName(false) << " NOT as auto-installed (direct " - << Start.DepType() << " of " << Pkg.FullName(false) << " which is manual and in APT::Move-Autobit-Sections)" << std::endl; - MarkAuto(InstPkg, false); - } + pkgCache::PkgIterator InstPkg = InstVer.ParentPkg(); + if (DebugAutoInstall) + std::clog << OutputInDepth(Depth) << "Installing " << InstPkg.Name() + << " as " << Start.DepType() << " of " << Pkg.Name() << '\n'; + if (not MarkInstall(InstPkg, true, Depth + 1, false, ForceImportantDeps)) + { + verlist.erase(InstVer); + continue; + } + // now check if we should consider it a automatic dependency or not + if (InstPkg->CurrentVer == 0 && MoveAutoBitToDependencies) + { + if (DebugAutoInstall == true) + std::clog << OutputInDepth(Depth) << "Setting " << InstPkg.FullName(false) << " NOT as auto-installed (direct " + << Start.DepType() << " of " << Pkg.FullName(false) << " which is manual and in APT::Move-Autobit-Sections)\n"; + MarkAuto(InstPkg, false); + } - break; - } while(true); - continue; + foundSolution = true; + break; + } while (true); + } + if (foundSolution) + continue; + break; } /* Negative dependencies have no or-group If the dependency isn't versioned, we try if an upgrade might solve the problem. -- cgit v1.2.3 From 924704ba1dc13f73edb0db6c3c8c07ccf0ec26b6 Mon Sep 17 00:00:00 2001 From: David Kalnischkies Date: Mon, 27 Apr 2020 13:45:41 +0200 Subject: Refactor and reorder MarkInstall code This fixes no bugs per se, but the idea is to delay more costly changes and check easier things first. It e.g. inhibits the moving of the autobit until we are sure that this MarkInstall call isn't going to fail (e.g. because a dependency isn't satisfiable). --- apt-pkg/depcache.cc | 279 +++++++++++++++++++++++++++------------------------- 1 file changed, 145 insertions(+), 134 deletions(-) (limited to 'apt-pkg/depcache.cc') diff --git a/apt-pkg/depcache.cc b/apt-pkg/depcache.cc index 3d510b7e6..cd5558461 100644 --- a/apt-pkg/depcache.cc +++ b/apt-pkg/depcache.cc @@ -1083,7 +1083,7 @@ bool pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst, /* Check that it is not already marked for install and that it can be installed */ - if ((P.InstPolicyBroken() == false && P.InstBroken() == false) && + if ((not P.InstPolicyBroken() && not P.InstBroken()) && (P.Mode == ModeInstall || P.CandidateVer == (Version *)Pkg.CurrentVer())) { @@ -1093,7 +1093,7 @@ bool pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst, } // check if we are allowed to install the package - if (IsInstallOk(Pkg,AutoInst,Depth,FromUser) == false) + if (not IsInstallOk(Pkg, AutoInst, Depth, FromUser)) return false; ActionGroup group(*this); @@ -1129,68 +1129,112 @@ bool pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst, Update(Pkg); AddSizes(Pkg); - if (AutoInst == false || _config->Find("APT::Solver", "internal") != "internal") + if (not AutoInst || _config->Find("APT::Solver", "internal") != "internal") return true; - if (DebugMarker == true) + if (DebugMarker) std::clog << OutputInDepth(Depth) << "MarkInstall " << APT::PrettyPkg(this, Pkg) << " FU=" << FromUser << std::endl; - bool MoveAutoBitToDependencies = false; + // collect dependencies we will have to resolve one way or the other VerIterator const PV = P.InstVerIter(*this); - if (unlikely(PV.end() == true)) + if (unlikely(PV.end())) return false; - else if (PV->Section != 0 && (P.Flags & Flag::Auto) != Flag::Auto) + + std::vector toRemove, toInstall; + for (auto Dep = PV.DependsList(); not Dep.end();) { - VerIterator const CurVer = Pkg.CurrentVer(); - if (CurVer.end() == false && CurVer->Section != 0 && strcmp(CurVer.Section(), PV.Section()) != 0) + auto const Start = Dep; + // check if an installed package satisfies the dependency (and get the extend of the or-group) + bool foundSolution = false; + for (bool LastOR = true; not Dep.end() && LastOR; ++Dep) { - bool const CurVerInMoveSection = ConfigValueInSubTree("APT::Move-Autobit-Sections", CurVer.Section()); - bool const InstVerInMoveSection = ConfigValueInSubTree("APT::Move-Autobit-Sections", PV.Section()); - MoveAutoBitToDependencies = (CurVerInMoveSection == false && InstVerInMoveSection == true); - if (MoveAutoBitToDependencies == true) + LastOR = (Dep->CompareOp & Dep::Or) == Dep::Or; + if ((DepState[Dep->ID] & DepInstall) == DepInstall) + foundSolution = true; + } + if (foundSolution) + continue; + + /* Check if this dep should be consider for install. + (Pre-)Depends, Conflicts and Breaks for sure. + Recommends & Suggests depending on configuration */ + if (not IsImportantDep(Start)) + continue; + + if (Start.IsNegative()) + { + if (Start->Type != pkgCache::Dep::Obsoletes) + toRemove.push_back(Start); + } + else + toInstall.push_back(Start); + } + + /* Negative dependencies have no or-group + If the dependency isn't versioned, we try if an upgrade might solve the problem. + Otherwise we remove the offender if needed */ + APT::PackageVector toUpgrade; + for (auto const &D : toRemove) + { + std::unique_ptr List(D.AllTargets()); + pkgCache::PkgIterator TrgPkg = D.TargetPkg(); + for (Version **I = List.get(); *I != 0; I++) + { + VerIterator Ver(*this, *I); + PkgIterator Pkg = Ver.ParentPkg(); + + /* The List includes all packages providing this dependency, + even providers which are not installed, so skip them. */ + if (PkgState[Pkg->ID].InstallVer == 0) + continue; + + // Ignore negative dependencies on versions that are not going to get installed + if (PkgState[Pkg->ID].InstallVer != *I) + continue; + + if ((D->Version != 0 || TrgPkg != Pkg) && + PkgState[Pkg->ID].CandidateVer != PkgState[Pkg->ID].InstallVer && + PkgState[Pkg->ID].CandidateVer != *I) + toUpgrade.push_back(Pkg); + else { if(DebugAutoInstall == true) - std::clog << OutputInDepth(Depth) << "Setting " << Pkg.FullName(false) << " as auto-installed, moving manual to its dependencies" << std::endl; - MarkAuto(Pkg, true); + std::clog << OutputInDepth(Depth) << " Removing: " << Pkg.Name() << " as upgrade is not an option\n"; + if (not MarkDelete(Pkg, false, Depth + 1, false)) + return false; } } } + toRemove.clear(); + for (auto const &InstPkg : toUpgrade) + if (not MarkInstall(InstPkg, true, Depth + 1, false, ForceImportantDeps)) + { + if (DebugAutoInstall) + std::clog << OutputInDepth(Depth) << " Removing: " << InstPkg.FullName() << " as upgrade is not possible\n"; + if (not MarkDelete(InstPkg, false, Depth + 1, false)) + return false; + } + toUpgrade.clear(); - DepIterator Dep = PV.DependsList(); - for (; Dep.end() != true;) + bool MoveAutoBitToDependencies = false; + if (PV->Section != 0 && (P.Flags & Flag::Auto) != Flag::Auto) { - // Grok or groups - DepIterator Start = Dep; - bool Result = true; - unsigned Ors = 0; - for (bool LastOR = true; Dep.end() == false && LastOR == true; ++Dep, ++Ors) + VerIterator const CurVer = Pkg.CurrentVer(); + if (not CurVer.end() && CurVer->Section != 0 && strcmp(CurVer.Section(), PV.Section()) != 0) { - LastOR = (Dep->CompareOp & Dep::Or) == Dep::Or; - - if ((DepState[Dep->ID] & DepInstall) == DepInstall) - Result = false; + bool const CurVerInMoveSection = ConfigValueInSubTree("APT::Move-Autobit-Sections", CurVer.Section()); + bool const InstVerInMoveSection = ConfigValueInSubTree("APT::Move-Autobit-Sections", PV.Section()); + MoveAutoBitToDependencies = (not CurVerInMoveSection && InstVerInMoveSection); } - - // Dep is satisfied okay. - if (Result == false) - continue; - - /* Check if this dep should be consider for install. If it is a user - defined important dep and we are installed a new package then - it will be installed. Otherwise we only check for important - deps that have changed from the installed version */ - if (IsImportantDep(Start) == false) - continue; - - /* If we are in an or group locate the first or that can - succeed. We have already cached this… */ - for (; Ors > 1 && (DepState[Start->ID] & DepCVer) != DepCVer; --Ors) - ++Start; + } + APT::PackageVector toMoveAuto; - /* unsatisfiable dependency: IsInstallOkDependenciesSatisfiableByCandidates - would have prevented us to get here if not overridden, so just skip - over the problem here as the front-end will know what it is doing */ - if (Ors == 1 && (DepState[Start->ID] &DepCVer) != DepCVer && Start.IsNegative() == false) + auto const IsSatisfiedByInstalled = [&](auto const D) { return (DepState[D.ID] & DepInstall) == DepInstall; }; + for (auto &&Dep : toInstall) + { + pkgDepCache::DepIterator Start, End; + Dep.GlobOr(Start, End); + if (std::any_of(Start, Dep, IsSatisfiedByInstalled)) continue; /* Check if any ImportantDep() (but not Critical) were added @@ -1200,7 +1244,7 @@ bool pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst, * package should follow that Recommends rather than causing the * dependency to be removed. (bug #470115) */ - if (Pkg->CurrentVer != 0 && ForceImportantDeps == false && Start.IsCritical() == false) + if (Pkg->CurrentVer != 0 && not ForceImportantDeps && not Start.IsCritical()) { bool isNewImportantDep = true; bool isPreviouslySatisfiedImportantDep = false; @@ -1209,7 +1253,7 @@ bool pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst, //FIXME: Should we handle or-group better here? // We do not check if the package we look for is part of the same or-group // we might find while searching, but could that really be a problem? - if (D.IsCritical() == true || IsImportantDep(D) == false || + if (D.IsCritical() || not IsImportantDep(D) || Start.TargetPkg() != D.TargetPkg()) continue; @@ -1219,118 +1263,85 @@ bool pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst, ++D; isPreviouslySatisfiedImportantDep = (((*this)[D] & DepGNow) != 0); - if (isPreviouslySatisfiedImportantDep == true) + if (isPreviouslySatisfiedImportantDep) break; } - if(isNewImportantDep == true) + if (isNewImportantDep) { - if (DebugAutoInstall == true) + if (DebugAutoInstall) std::clog << OutputInDepth(Depth) << "new important dependency: " - << Start.TargetPkg().FullName() << std::endl; + << Start.TargetPkg().FullName() << '\n'; } - else if(isPreviouslySatisfiedImportantDep == true) + else if (isPreviouslySatisfiedImportantDep) { - if (DebugAutoInstall == true) + if (DebugAutoInstall) std::clog << OutputInDepth(Depth) << "previously satisfied important dependency on " - << Start.TargetPkg().FullName() << std::endl; + << Start.TargetPkg().FullName() << '\n'; } else { - if (DebugAutoInstall == true) + if (DebugAutoInstall) std::clog << OutputInDepth(Depth) << "ignore old unsatisfied important dependency on " - << Start.TargetPkg().FullName() << std::endl; + << Start.TargetPkg().FullName() << '\n'; continue; } } - /* This bit is for processing the possibility of an install/upgrade - fixing the problem for "positive" dependencies */ - if (not Start.IsNegative() && (DepState[Start->ID] & DepCVer) == DepCVer) + bool foundSolution = false; + do { - bool foundSolution = false; - for (; Start != Dep && not foundSolution; ++Start) - { - pkgCacheFile CacheFile(this); - APT::VersionList verlist = APT::VersionList::FromDependency(CacheFile, Start, APT::CacheSetHelper::CANDIDATE); - CompareProviders comp(Start); - - do - { - APT::VersionList::iterator InstVer = std::max_element(verlist.begin(), verlist.end(), comp); - if (InstVer == verlist.end()) - break; - - pkgCache::PkgIterator InstPkg = InstVer.ParentPkg(); - if (DebugAutoInstall) - std::clog << OutputInDepth(Depth) << "Installing " << InstPkg.Name() - << " as " << Start.DepType() << " of " << Pkg.Name() << '\n'; - if (not MarkInstall(InstPkg, true, Depth + 1, false, ForceImportantDeps)) - { - verlist.erase(InstVer); - continue; - } + if ((DepState[Start->ID] & DepCVer) != DepCVer) + continue; - // now check if we should consider it a automatic dependency or not - if (InstPkg->CurrentVer == 0 && MoveAutoBitToDependencies) - { - if (DebugAutoInstall == true) - std::clog << OutputInDepth(Depth) << "Setting " << InstPkg.FullName(false) << " NOT as auto-installed (direct " - << Start.DepType() << " of " << Pkg.FullName(false) << " which is manual and in APT::Move-Autobit-Sections)\n"; - MarkAuto(InstPkg, false); - } + pkgCacheFile CacheFile(this); + APT::VersionList verlist = APT::VersionList::FromDependency(CacheFile, Start, APT::CacheSetHelper::CANDIDATE); + CompareProviders comp(Start); - foundSolution = true; - break; - } while (true); - } - if (foundSolution) - continue; - break; - } - /* Negative dependencies have no or-group - If the dependency isn't versioned, we try if an upgrade might solve the problem. - Otherwise we remove the offender if needed */ - else if (Start.IsNegative() == true && Start->Type != pkgCache::Dep::Obsoletes) - { - std::unique_ptr List(Start.AllTargets()); - pkgCache::PkgIterator TrgPkg = Start.TargetPkg(); - for (Version **I = List.get(); *I != 0; I++) + do { - VerIterator Ver(*this,*I); - PkgIterator Pkg = Ver.ParentPkg(); + APT::VersionList::iterator InstVer = std::max_element(verlist.begin(), verlist.end(), comp); + if (InstVer == verlist.end()) + break; - /* The List includes all packages providing this dependency, - even providers which are not installed, so skip them. */ - if (PkgState[Pkg->ID].InstallVer == 0) + pkgCache::PkgIterator InstPkg = InstVer.ParentPkg(); + if (DebugAutoInstall) + std::clog << OutputInDepth(Depth) << "Installing " << InstPkg.Name() + << " as " << Start.DepType() << " of " << Pkg.Name() << '\n'; + if (not MarkInstall(InstPkg, true, Depth + 1, false, ForceImportantDeps)) + { + verlist.erase(InstVer); continue; + } - /* Ignore negative dependencies that we are not going to - get installed */ - if (PkgState[Pkg->ID].InstallVer != *I) - continue; + if (MoveAutoBitToDependencies && InstPkg->CurrentVer == 0) + toMoveAuto.push_back(InstPkg); - if ((Start->Version != 0 || TrgPkg != Pkg) && - PkgState[Pkg->ID].CandidateVer != PkgState[Pkg->ID].InstallVer && - PkgState[Pkg->ID].CandidateVer != *I && - MarkInstall(Pkg,true,Depth + 1, false, ForceImportantDeps) == true) - continue; - else if (Start->Type == pkgCache::Dep::Conflicts || - Start->Type == pkgCache::Dep::DpkgBreaks) - { - if(DebugAutoInstall == true) - std::clog << OutputInDepth(Depth) - << " Removing: " << Pkg.Name() - << std::endl; - if (MarkDelete(Pkg,false,Depth + 1, false) == false) - break; - } - } - continue; - } + foundSolution = true; + break; + } while (true); + if (foundSolution) + break; + } while (Start++ != End); + if (not foundSolution && End.IsCritical()) + return false; } + toInstall.clear(); - return Dep.end() == true; + if (MoveAutoBitToDependencies) + { + if (DebugAutoInstall) + std::clog << OutputInDepth(Depth) << "Setting " << Pkg.FullName(false) << " as auto-installed, moving manual to its dependencies" << std::endl; + MarkAuto(Pkg, true); + for (auto const &InstPkg : toMoveAuto) + { + if (DebugAutoInstall) + std::clog << OutputInDepth(Depth) << "Setting " << InstPkg.FullName(false) << " NOT as auto-installed (dependency" + << " of " << Pkg.FullName(false) << " which is manual and in APT::Move-Autobit-Sections)\n"; + MarkAuto(InstPkg, false); + } + } + return true; } /*}}}*/ // DepCache::IsInstallOk - check if it is ok to install this package /*{{{*/ -- cgit v1.2.3 From 0de399391372450d0162b5a09bfca554b2d27c3d Mon Sep 17 00:00:00 2001 From: David Kalnischkies Date: Sun, 26 Apr 2020 13:11:31 +0200 Subject: Discard candidate if its dependencies can't be satisfied We do pretty much the same in IsInstallOk, but here we have already set the state, so we have to unroll the state as well to sort-of replicate the state we were in before this MarkInstall failed. --- apt-pkg/depcache.cc | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'apt-pkg/depcache.cc') diff --git a/apt-pkg/depcache.cc b/apt-pkg/depcache.cc index cd5558461..425918b43 100644 --- a/apt-pkg/depcache.cc +++ b/apt-pkg/depcache.cc @@ -1324,7 +1324,24 @@ bool pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst, break; } while (Start++ != End); if (not foundSolution && End.IsCritical()) + { + StateCache &State = PkgState[Pkg->ID]; + if (not State.Protect()) + { + RemoveSizes(Pkg); + RemoveStates(Pkg); + if (Pkg->CurrentVer != 0) + State.InstallVer = State.CandidateVer = Pkg.CurrentVer(); + else + State.InstallVer = State.CandidateVer = nullptr; + State.Mode = ModeKeep; + State.Update(Pkg, *this); + AddStates(Pkg); + Update(Pkg); + AddSizes(Pkg); + } return false; + } } toInstall.clear(); -- cgit v1.2.3 From 76498d46855c88b90316e4369ac32050db9a9d23 Mon Sep 17 00:00:00 2001 From: David Kalnischkies Date: Sun, 26 Apr 2020 13:14:43 +0200 Subject: Split up MarkInstall into private helper methods Should be easier to move the code bits around then and it helps in documenting a bit what the blocks do and how they interact (or not). --- apt-pkg/depcache.cc | 165 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 101 insertions(+), 64 deletions(-) (limited to 'apt-pkg/depcache.cc') diff --git a/apt-pkg/depcache.cc b/apt-pkg/depcache.cc index 425918b43..b88e9ceb8 100644 --- a/apt-pkg/depcache.cc +++ b/apt-pkg/depcache.cc @@ -980,10 +980,8 @@ bool pkgDepCache::IsModeChangeOk(ModeList const mode, PkgIterator const &Pkg, return true; } /*}}}*/ -// DepCache::MarkInstall - Put the package in the install state /*{{{*/ -// --------------------------------------------------------------------- -/* */ -struct CompareProviders { +struct CompareProviders /*{{{*/ +{ pkgCache::PkgIterator const Pkg; explicit CompareProviders(pkgCache::DepIterator const &Dep) : Pkg(Dep.TargetPkg()) {}; //bool operator() (APT::VersionList::iterator const &AV, APT::VersionList::iterator const &BV) @@ -1068,35 +1066,10 @@ struct CompareProviders { return A->ID < B->ID; } }; -bool pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst, - unsigned long Depth, bool FromUser, - bool ForceImportantDeps) + /*}}}*/ +bool pkgDepCache::MarkInstall_StateChange(PkgIterator const &Pkg, bool AutoInst, bool FromUser) /*{{{*/ { - if (IsModeChangeOk(ModeInstall, Pkg, Depth, FromUser) == false) - return false; - StateCache &P = PkgState[Pkg->ID]; - - // See if there is even any possible installation candidate - if (P.CandidateVer == 0) - return false; - - /* Check that it is not already marked for install and that it can be - installed */ - if ((not P.InstPolicyBroken() && not P.InstBroken()) && - (P.Mode == ModeInstall || - P.CandidateVer == (Version *)Pkg.CurrentVer())) - { - if (P.CandidateVer == (Version *)Pkg.CurrentVer() && P.InstallVer == 0) - return MarkKeep(Pkg, false, FromUser, Depth+1); - return true; - } - - // check if we are allowed to install the package - if (not IsInstallOk(Pkg, AutoInst, Depth, FromUser)) - return false; - - ActionGroup group(*this); P.iFlags &= ~AutoKept; /* Target the candidate version and remove the autoflag. We reset the @@ -1104,7 +1077,7 @@ bool pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst, should have the ability to de-auto a package by changing its state */ RemoveSizes(Pkg); RemoveStates(Pkg); - + P.Mode = ModeInstall; P.InstallVer = P.CandidateVer; @@ -1124,23 +1097,15 @@ bool pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst, } if (P.CandidateVer == (Version *)Pkg.CurrentVer()) P.Mode = ModeKeep; - + AddStates(Pkg); Update(Pkg); AddSizes(Pkg); - - if (not AutoInst || _config->Find("APT::Solver", "internal") != "internal") - return true; - - if (DebugMarker) - std::clog << OutputInDepth(Depth) << "MarkInstall " << APT::PrettyPkg(this, Pkg) << " FU=" << FromUser << std::endl; - - // collect dependencies we will have to resolve one way or the other - VerIterator const PV = P.InstVerIter(*this); - if (unlikely(PV.end())) - return false; - - std::vector toRemove, toInstall; + return true; +} + /*}}}*/ +bool pkgDepCache::MarkInstall_CollectDependencies(pkgCache::VerIterator const &PV, std::vector &toInstall, std::vector &toRemove) /*{{{*/ +{ for (auto Dep = PV.DependsList(); not Dep.end();) { auto const Start = Dep; @@ -1169,11 +1134,14 @@ bool pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst, else toInstall.push_back(Start); } - + return true; +} + /*}}}*/ +bool pkgDepCache::MarkInstall_RemoveConflictsIfNotUpgradeable(unsigned long Depth, std::vector &toRemove, APT::PackageVector &toUpgrade) /*{{{*/ +{ /* Negative dependencies have no or-group If the dependency isn't versioned, we try if an upgrade might solve the problem. Otherwise we remove the offender if needed */ - APT::PackageVector toUpgrade; for (auto const &D : toRemove) { std::unique_ptr List(D.AllTargets()); @@ -1206,6 +1174,11 @@ bool pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst, } } toRemove.clear(); + return true; +} + /*}}}*/ +bool pkgDepCache::MarkInstall_UpgradeOrRemoveConflicts(unsigned long Depth, bool const ForceImportantDeps, APT::PackageVector &toUpgrade) /*{{{*/ +{ for (auto const &InstPkg : toUpgrade) if (not MarkInstall(InstPkg, true, Depth + 1, false, ForceImportantDeps)) { @@ -1215,20 +1188,11 @@ bool pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst, return false; } toUpgrade.clear(); - - bool MoveAutoBitToDependencies = false; - if (PV->Section != 0 && (P.Flags & Flag::Auto) != Flag::Auto) - { - VerIterator const CurVer = Pkg.CurrentVer(); - if (not CurVer.end() && CurVer->Section != 0 && strcmp(CurVer.Section(), PV.Section()) != 0) - { - bool const CurVerInMoveSection = ConfigValueInSubTree("APT::Move-Autobit-Sections", CurVer.Section()); - bool const InstVerInMoveSection = ConfigValueInSubTree("APT::Move-Autobit-Sections", PV.Section()); - MoveAutoBitToDependencies = (not CurVerInMoveSection && InstVerInMoveSection); - } - } - APT::PackageVector toMoveAuto; - + return true; +} + /*}}}*/ +bool pkgDepCache::MarkInstall_InstallDependencies(PkgIterator const &Pkg, unsigned long Depth, bool const ForceImportantDeps, std::vector &toInstall, APT::PackageVector *const toMoveAuto) /*{{{*/ +{ auto const IsSatisfiedByInstalled = [&](auto const D) { return (DepState[D.ID] & DepInstall) == DepInstall; }; for (auto &&Dep : toInstall) { @@ -1314,8 +1278,8 @@ bool pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst, continue; } - if (MoveAutoBitToDependencies && InstPkg->CurrentVer == 0) - toMoveAuto.push_back(InstPkg); + if (toMoveAuto != nullptr && InstPkg->CurrentVer == 0) + toMoveAuto->push_back(InstPkg); foundSolution = true; break; @@ -1344,6 +1308,79 @@ bool pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst, } } toInstall.clear(); + return true; +} + /*}}}*/ +// DepCache::MarkInstall - Put the package in the install state /*{{{*/ +bool pkgDepCache::MarkInstall(PkgIterator const &Pkg, bool AutoInst, + unsigned long Depth, bool FromUser, + bool ForceImportantDeps) +{ + if (IsModeChangeOk(ModeInstall, Pkg, Depth, FromUser) == false) + return false; + + StateCache &P = PkgState[Pkg->ID]; + + // See if there is even any possible installation candidate + if (P.CandidateVer == 0) + return false; + + /* Check that it is not already marked for install and that it can be + installed */ + if ((not P.InstPolicyBroken() && not P.InstBroken()) && + (P.Mode == ModeInstall || + P.CandidateVer == (Version *)Pkg.CurrentVer())) + { + if (P.CandidateVer == (Version *)Pkg.CurrentVer() && P.InstallVer == 0) + return MarkKeep(Pkg, false, FromUser, Depth + 1); + return true; + } + + // check if we are allowed to install the package + if (not IsInstallOk(Pkg, AutoInst, Depth, FromUser)) + return false; + + ActionGroup group(*this); + if (not MarkInstall_StateChange(Pkg, AutoInst, FromUser)) + return false; + + if (not AutoInst || _config->Find("APT::Solver", "internal") != "internal") + return true; + + if (DebugMarker) + std::clog << OutputInDepth(Depth) << "MarkInstall " << APT::PrettyPkg(this, Pkg) << " FU=" << FromUser << std::endl; + + VerIterator const PV = P.InstVerIter(*this); + if (unlikely(PV.end())) + return false; + + std::vector toInstall, toRemove; + if (not MarkInstall_CollectDependencies(PV, toInstall, toRemove)) + return false; + + APT::PackageVector toUpgrade; + if (not MarkInstall_RemoveConflictsIfNotUpgradeable(Depth, toRemove, toUpgrade)) + return false; + + if (not MarkInstall_UpgradeOrRemoveConflicts(Depth, ForceImportantDeps, toUpgrade)) + return false; + + bool const MoveAutoBitToDependencies = [&]() { + if (PV->Section == 0 || (P.Flags & Flag::Auto) == Flag::Auto) + return false; + VerIterator const CurVer = Pkg.CurrentVer(); + if (not CurVer.end() && CurVer->Section != 0 && strcmp(CurVer.Section(), PV.Section()) != 0) + { + bool const CurVerInMoveSection = ConfigValueInSubTree("APT::Move-Autobit-Sections", CurVer.Section()); + bool const InstVerInMoveSection = ConfigValueInSubTree("APT::Move-Autobit-Sections", PV.Section()); + return (not CurVerInMoveSection && InstVerInMoveSection); + } + return false; + }(); + + APT::PackageVector toMoveAuto; + if (not MarkInstall_InstallDependencies(Pkg, Depth, ForceImportantDeps, toInstall, MoveAutoBitToDependencies ? &toMoveAuto : nullptr)) + return false; if (MoveAutoBitToDependencies) { -- cgit v1.2.3 From 347ea3f76ab263c729468e07b910ae027b66c9d8 Mon Sep 17 00:00:00 2001 From: David Kalnischkies Date: Mon, 27 Apr 2020 13:48:33 +0200 Subject: Fail earlier on impossible Conflicts in MarkInstall MarkDelete is not recursive as MarkInstall is and we can not conflict with ourselves anyhow, so we can move the unavoidable deletes before changing the state of the package in question avoiding the need for the state update in case of conflicts we can not deal with (e.g. the package conflicts with an explicit user request). --- apt-pkg/depcache.cc | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) (limited to 'apt-pkg/depcache.cc') diff --git a/apt-pkg/depcache.cc b/apt-pkg/depcache.cc index b88e9ceb8..4b2da4b4b 100644 --- a/apt-pkg/depcache.cc +++ b/apt-pkg/depcache.cc @@ -1137,7 +1137,7 @@ bool pkgDepCache::MarkInstall_CollectDependencies(pkgCache::VerIterator const &P return true; } /*}}}*/ -bool pkgDepCache::MarkInstall_RemoveConflictsIfNotUpgradeable(unsigned long Depth, std::vector &toRemove, APT::PackageVector &toUpgrade) /*{{{*/ +bool pkgDepCache::MarkInstall_RemoveConflictsIfNotUpgradeable(pkgCache::VerIterator const &PV, unsigned long Depth, std::vector &toRemove, APT::PackageVector &toUpgrade) /*{{{*/ { /* Negative dependencies have no or-group If the dependency isn't versioned, we try if an upgrade might solve the problem. @@ -1167,7 +1167,7 @@ bool pkgDepCache::MarkInstall_RemoveConflictsIfNotUpgradeable(unsigned long Dept else { if(DebugAutoInstall == true) - std::clog << OutputInDepth(Depth) << " Removing: " << Pkg.Name() << " as upgrade is not an option\n"; + std::clog << OutputInDepth(Depth) << " Removing: " << Pkg.Name() << " as upgrade is not an option for " << PV.ParentPkg().FullName() << "(" << PV.VerStr() << ")\n"; if (not MarkDelete(Pkg, false, Depth + 1, false)) return false; } @@ -1340,32 +1340,39 @@ bool pkgDepCache::MarkInstall(PkgIterator const &Pkg, bool AutoInst, if (not IsInstallOk(Pkg, AutoInst, Depth, FromUser)) return false; + bool const AutoSolve = AutoInst && _config->Find("APT::Solver", "internal") == "internal"; + + std::vector toInstall, toRemove; + APT::PackageVector toUpgrade; + if (AutoSolve) + { + VerIterator const PV = P.CandidateVerIter(*this); + if (unlikely(PV.end())) + return false; + if (not MarkInstall_CollectDependencies(PV, toInstall, toRemove)) + return false; + + if (not MarkInstall_RemoveConflictsIfNotUpgradeable(PV, Depth, toRemove, toUpgrade)) + return false; + } + ActionGroup group(*this); if (not MarkInstall_StateChange(Pkg, AutoInst, FromUser)) return false; - if (not AutoInst || _config->Find("APT::Solver", "internal") != "internal") + if (not AutoSolve) return true; if (DebugMarker) - std::clog << OutputInDepth(Depth) << "MarkInstall " << APT::PrettyPkg(this, Pkg) << " FU=" << FromUser << std::endl; - - VerIterator const PV = P.InstVerIter(*this); - if (unlikely(PV.end())) - return false; - - std::vector toInstall, toRemove; - if (not MarkInstall_CollectDependencies(PV, toInstall, toRemove)) - return false; - - APT::PackageVector toUpgrade; - if (not MarkInstall_RemoveConflictsIfNotUpgradeable(Depth, toRemove, toUpgrade)) - return false; + std::clog << OutputInDepth(Depth) << "MarkInstall " << APT::PrettyPkg(this, Pkg) << " FU=" << FromUser << '\n'; if (not MarkInstall_UpgradeOrRemoveConflicts(Depth, ForceImportantDeps, toUpgrade)) return false; bool const MoveAutoBitToDependencies = [&]() { + VerIterator const PV = P.InstVerIter(*this); + if (unlikely(PV.end())) + return false; if (PV->Section == 0 || (P.Flags & Flag::Auto) == Flag::Auto) return false; VerIterator const CurVer = Pkg.CurrentVer(); -- cgit v1.2.3 From f76a8d331a81bc7b102bdd4e0f8363e8a59f64f6 Mon Sep 17 00:00:00 2001 From: David Kalnischkies Date: Mon, 27 Apr 2020 13:49:19 +0200 Subject: Propagate Protected flag to single-option dependencies If a package is protected and has a dependency satisfied only by a single package (or conflicts with a package) this package must be part of the solution and so we can help later actions not exploring dead ends by propagating the protected flag to these "pseudo-protected" packages. An (obscure) bug this can help prevent (to some extend) is shown in test-apt-never-markauto-sections by not causing irreversible autobit transfers. As a sideeffect it seems also to help our crude ShowBroken to display slightly more helpful messages involving the packages which are actually in conflict. --- apt-pkg/depcache.cc | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) (limited to 'apt-pkg/depcache.cc') diff --git a/apt-pkg/depcache.cc b/apt-pkg/depcache.cc index 4b2da4b4b..03fc58f69 100644 --- a/apt-pkg/depcache.cc +++ b/apt-pkg/depcache.cc @@ -1170,6 +1170,8 @@ bool pkgDepCache::MarkInstall_RemoveConflictsIfNotUpgradeable(pkgCache::VerItera std::clog << OutputInDepth(Depth) << " Removing: " << Pkg.Name() << " as upgrade is not an option for " << PV.ParentPkg().FullName() << "(" << PV.VerStr() << ")\n"; if (not MarkDelete(Pkg, false, Depth + 1, false)) return false; + if (PkgState[PV.ParentPkg()->ID].Protect()) + MarkProtected(Pkg); } } } @@ -1177,7 +1179,7 @@ bool pkgDepCache::MarkInstall_RemoveConflictsIfNotUpgradeable(pkgCache::VerItera return true; } /*}}}*/ -bool pkgDepCache::MarkInstall_UpgradeOrRemoveConflicts(unsigned long Depth, bool const ForceImportantDeps, APT::PackageVector &toUpgrade) /*{{{*/ +bool pkgDepCache::MarkInstall_UpgradeOrRemoveConflicts(PkgIterator const &Pkg, unsigned long Depth, bool const ForceImportantDeps, APT::PackageVector &toUpgrade) /*{{{*/ { for (auto const &InstPkg : toUpgrade) if (not MarkInstall(InstPkg, true, Depth + 1, false, ForceImportantDeps)) @@ -1186,6 +1188,8 @@ bool pkgDepCache::MarkInstall_UpgradeOrRemoveConflicts(unsigned long Depth, bool std::clog << OutputInDepth(Depth) << " Removing: " << InstPkg.FullName() << " as upgrade is not possible\n"; if (not MarkDelete(InstPkg, false, Depth + 1, false)) return false; + if (PkgState[Pkg->ID].Protect()) + MarkProtected(InstPkg); } toUpgrade.clear(); return true; @@ -1200,6 +1204,7 @@ bool pkgDepCache::MarkInstall_InstallDependencies(PkgIterator const &Pkg, unsign Dep.GlobOr(Start, End); if (std::any_of(Start, Dep, IsSatisfiedByInstalled)) continue; + bool const IsCriticalDep = Start.IsCritical(); /* Check if any ImportantDep() (but not Critical) were added * since we installed the package. Also check for deps that @@ -1208,7 +1213,7 @@ bool pkgDepCache::MarkInstall_InstallDependencies(PkgIterator const &Pkg, unsign * package should follow that Recommends rather than causing the * dependency to be removed. (bug #470115) */ - if (Pkg->CurrentVer != 0 && not ForceImportantDeps && not Start.IsCritical()) + if (Pkg->CurrentVer != 0 && not ForceImportantDeps && not IsCriticalDep) { bool isNewImportantDep = true; bool isPreviouslySatisfiedImportantDep = false; @@ -1253,6 +1258,7 @@ bool pkgDepCache::MarkInstall_InstallDependencies(PkgIterator const &Pkg, unsign } bool foundSolution = false; + bool thereIsOnlyOne1 = Start == End; do { if ((DepState[Start->ID] & DepCVer) != DepCVer) @@ -1261,6 +1267,7 @@ bool pkgDepCache::MarkInstall_InstallDependencies(PkgIterator const &Pkg, unsign pkgCacheFile CacheFile(this); APT::VersionList verlist = APT::VersionList::FromDependency(CacheFile, Start, APT::CacheSetHelper::CANDIDATE); CompareProviders comp(Start); + bool thereIsOnlyOne2 = thereIsOnlyOne1 && verlist.size() == 1; do { @@ -1272,6 +1279,15 @@ bool pkgDepCache::MarkInstall_InstallDependencies(PkgIterator const &Pkg, unsign if (DebugAutoInstall) std::clog << OutputInDepth(Depth) << "Installing " << InstPkg.Name() << " as " << Start.DepType() << " of " << Pkg.Name() << '\n'; + if (thereIsOnlyOne2 && PkgState[Pkg->ID].Protect() && IsCriticalDep) + { + if (not MarkInstall(InstPkg, false, Depth + 1, false, ForceImportantDeps)) + { + verlist.erase(InstVer); + continue; + } + MarkProtected(InstPkg); + } if (not MarkInstall(InstPkg, true, Depth + 1, false, ForceImportantDeps)) { verlist.erase(InstVer); @@ -1287,7 +1303,7 @@ bool pkgDepCache::MarkInstall_InstallDependencies(PkgIterator const &Pkg, unsign if (foundSolution) break; } while (Start++ != End); - if (not foundSolution && End.IsCritical()) + if (not foundSolution && IsCriticalDep) { StateCache &State = PkgState[Pkg->ID]; if (not State.Protect()) @@ -1366,7 +1382,7 @@ bool pkgDepCache::MarkInstall(PkgIterator const &Pkg, bool AutoInst, if (DebugMarker) std::clog << OutputInDepth(Depth) << "MarkInstall " << APT::PrettyPkg(this, Pkg) << " FU=" << FromUser << '\n'; - if (not MarkInstall_UpgradeOrRemoveConflicts(Depth, ForceImportantDeps, toUpgrade)) + if (not MarkInstall_UpgradeOrRemoveConflicts(Pkg, Depth, ForceImportantDeps, toUpgrade)) return false; bool const MoveAutoBitToDependencies = [&]() { -- cgit v1.2.3 From ca14e1e2c3f3c9782f374757ca4605ce7e5670ad Mon Sep 17 00:00:00 2001 From: David Kalnischkies Date: Sun, 26 Apr 2020 21:09:14 +0200 Subject: Prefer upgrading installed orgroup members MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In normal upgrade scenarios this is no problem as the orgroup member will be marked for upgrade already, but on a not fully upgraded system (or while you operate on a different target release) we would go with our usual "first come first serve" approach which might lead us to install another provider who comes earlier – bad if the providers conflict. --- apt-pkg/depcache.cc | 98 ++++++++++++++++++++++++++--------------------------- 1 file changed, 48 insertions(+), 50 deletions(-) (limited to 'apt-pkg/depcache.cc') diff --git a/apt-pkg/depcache.cc b/apt-pkg/depcache.cc index 03fc58f69..2254a9b5d 100644 --- a/apt-pkg/depcache.cc +++ b/apt-pkg/depcache.cc @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -1018,52 +1019,52 @@ struct CompareProviders /*{{{*/ } } if (instA != instB) - return instA == false; + return instA; } if ((A->CurrentVer == 0 || B->CurrentVer == 0) && A->CurrentVer != B->CurrentVer) - return A->CurrentVer == 0; + return A->CurrentVer != 0; // Prefer packages in the same group as the target; e.g. foo:i386, foo:amd64 if (A->Group != B->Group) { if (A->Group == Pkg->Group && B->Group != Pkg->Group) - return false; - else if (B->Group == Pkg->Group && A->Group != Pkg->Group) return true; + else if (B->Group == Pkg->Group && A->Group != Pkg->Group) + return false; } // we like essentials if ((A->Flags & pkgCache::Flag::Essential) != (B->Flags & pkgCache::Flag::Essential)) { if ((A->Flags & pkgCache::Flag::Essential) == pkgCache::Flag::Essential) - return false; - else if ((B->Flags & pkgCache::Flag::Essential) == pkgCache::Flag::Essential) return true; + else if ((B->Flags & pkgCache::Flag::Essential) == pkgCache::Flag::Essential) + return false; } if ((A->Flags & pkgCache::Flag::Important) != (B->Flags & pkgCache::Flag::Important)) { if ((A->Flags & pkgCache::Flag::Important) == pkgCache::Flag::Important) - return false; - else if ((B->Flags & pkgCache::Flag::Important) == pkgCache::Flag::Important) return true; + else if ((B->Flags & pkgCache::Flag::Important) == pkgCache::Flag::Important) + return false; } // prefer native architecture if (strcmp(A.Arch(), B.Arch()) != 0) { if (strcmp(A.Arch(), A.Cache()->NativeArch()) == 0) - return false; - else if (strcmp(B.Arch(), B.Cache()->NativeArch()) == 0) return true; + else if (strcmp(B.Arch(), B.Cache()->NativeArch()) == 0) + return false; std::vector archs = APT::Configuration::getArchitectures(); for (std::vector::const_iterator a = archs.begin(); a != archs.end(); ++a) if (*a == A.Arch()) - return false; - else if (*a == B.Arch()) return true; + else if (*a == B.Arch()) + return false; } // higher priority seems like a good idea if (AV->Priority != BV->Priority) - return AV->Priority > BV->Priority; + return AV->Priority < BV->Priority; // unable to decide… - return A->ID < B->ID; + return A->ID > B->ID; } }; /*}}}*/ @@ -1257,52 +1258,49 @@ bool pkgDepCache::MarkInstall_InstallDependencies(PkgIterator const &Pkg, unsign } } - bool foundSolution = false; - bool thereIsOnlyOne1 = Start == End; + pkgCacheFile CacheFile{this}; + APT::PackageVector toUpgrade, toNewInstall; do { if ((DepState[Start->ID] & DepCVer) != DepCVer) continue; - pkgCacheFile CacheFile(this); - APT::VersionList verlist = APT::VersionList::FromDependency(CacheFile, Start, APT::CacheSetHelper::CANDIDATE); - CompareProviders comp(Start); - bool thereIsOnlyOne2 = thereIsOnlyOne1 && verlist.size() == 1; - - do + APT::VersionVector verlist = APT::VersionVector::FromDependency(CacheFile, Start, APT::CacheSetHelper::CANDIDATE); + std::sort(verlist.begin(), verlist.end(), CompareProviders{Start}); + for (auto const &Ver : verlist) { - APT::VersionList::iterator InstVer = std::max_element(verlist.begin(), verlist.end(), comp); - if (InstVer == verlist.end()) - break; + auto P = Ver.ParentPkg(); + if (P->CurrentVer != 0) + toUpgrade.emplace_back(std::move(P)); + else + toNewInstall.emplace_back(std::move(P)); + } + } while (Start++ != End); - pkgCache::PkgIterator InstPkg = InstVer.ParentPkg(); - if (DebugAutoInstall) - std::clog << OutputInDepth(Depth) << "Installing " << InstPkg.Name() - << " as " << Start.DepType() << " of " << Pkg.Name() << '\n'; - if (thereIsOnlyOne2 && PkgState[Pkg->ID].Protect() && IsCriticalDep) - { - if (not MarkInstall(InstPkg, false, Depth + 1, false, ForceImportantDeps)) - { - verlist.erase(InstVer); - continue; - } - MarkProtected(InstPkg); - } - if (not MarkInstall(InstPkg, true, Depth + 1, false, ForceImportantDeps)) - { - verlist.erase(InstVer); + std::move(toNewInstall.begin(), toNewInstall.end(), std::back_inserter(toUpgrade)); + bool foundSolution = false; + for (auto const &InstPkg : toUpgrade) + { + if (PkgState[InstPkg->ID].CandidateVer == nullptr || PkgState[InstPkg->ID].CandidateVer == InstPkg.CurrentVer()) + continue; + if (DebugAutoInstall) + std::clog << OutputInDepth(Depth) << "Installing " << InstPkg.FullName() + << " as " << End.DepType() << " of " << Pkg.FullName() << '\n'; + if (IsCriticalDep && toUpgrade.size() == 1 && PkgState[Pkg->ID].Protect()) + { + if (not MarkInstall(InstPkg, false, Depth + 1, false, ForceImportantDeps)) continue; - } + MarkProtected(InstPkg); + } + if (not MarkInstall(InstPkg, true, Depth + 1, false, ForceImportantDeps)) + continue; - if (toMoveAuto != nullptr && InstPkg->CurrentVer == 0) - toMoveAuto->push_back(InstPkg); + if (toMoveAuto != nullptr && InstPkg->CurrentVer == 0) + toMoveAuto->push_back(InstPkg); - foundSolution = true; - break; - } while (true); - if (foundSolution) - break; - } while (Start++ != End); + foundSolution = true; + break; + } if (not foundSolution && IsCriticalDep) { StateCache &State = PkgState[Pkg->ID]; -- cgit v1.2.3 From ae23e53f99ea0b7920744a7303fdee64796b7cce Mon Sep 17 00:00:00 2001 From: David Kalnischkies Date: Mon, 27 Apr 2020 13:51:46 +0200 Subject: Protect a package while resolving in MarkInstall MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Strange things happen if while resolving the dependencies of a package said dependencies want to remove the package. The allow-scores test e.g. removed the preferred alternative in favor of the last one now that they were exclusive. In our or-group for Recommends we would "just" not statisfy the Recommends and for Depends we engage the ProblemResolver… --- apt-pkg/depcache.cc | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) (limited to 'apt-pkg/depcache.cc') diff --git a/apt-pkg/depcache.cc b/apt-pkg/depcache.cc index 2254a9b5d..7730aaf5b 100644 --- a/apt-pkg/depcache.cc +++ b/apt-pkg/depcache.cc @@ -1180,7 +1180,7 @@ bool pkgDepCache::MarkInstall_RemoveConflictsIfNotUpgradeable(pkgCache::VerItera return true; } /*}}}*/ -bool pkgDepCache::MarkInstall_UpgradeOrRemoveConflicts(PkgIterator const &Pkg, unsigned long Depth, bool const ForceImportantDeps, APT::PackageVector &toUpgrade) /*{{{*/ +bool pkgDepCache::MarkInstall_UpgradeOrRemoveConflicts(bool const propagateProctected, unsigned long Depth, bool const ForceImportantDeps, APT::PackageVector &toUpgrade) /*{{{*/ { for (auto const &InstPkg : toUpgrade) if (not MarkInstall(InstPkg, true, Depth + 1, false, ForceImportantDeps)) @@ -1189,14 +1189,14 @@ bool pkgDepCache::MarkInstall_UpgradeOrRemoveConflicts(PkgIterator const &Pkg, u std::clog << OutputInDepth(Depth) << " Removing: " << InstPkg.FullName() << " as upgrade is not possible\n"; if (not MarkDelete(InstPkg, false, Depth + 1, false)) return false; - if (PkgState[Pkg->ID].Protect()) + if (propagateProctected) MarkProtected(InstPkg); } toUpgrade.clear(); return true; } /*}}}*/ -bool pkgDepCache::MarkInstall_InstallDependencies(PkgIterator const &Pkg, unsigned long Depth, bool const ForceImportantDeps, std::vector &toInstall, APT::PackageVector *const toMoveAuto) /*{{{*/ +bool pkgDepCache::MarkInstall_InstallDependencies(PkgIterator const &Pkg, unsigned long Depth, bool const ForceImportantDeps, std::vector &toInstall, APT::PackageVector *const toMoveAuto, bool const propagateProctected) /*{{{*/ { auto const IsSatisfiedByInstalled = [&](auto const D) { return (DepState[D.ID] & DepInstall) == DepInstall; }; for (auto &&Dep : toInstall) @@ -1286,7 +1286,7 @@ bool pkgDepCache::MarkInstall_InstallDependencies(PkgIterator const &Pkg, unsign if (DebugAutoInstall) std::clog << OutputInDepth(Depth) << "Installing " << InstPkg.FullName() << " as " << End.DepType() << " of " << Pkg.FullName() << '\n'; - if (IsCriticalDep && toUpgrade.size() == 1 && PkgState[Pkg->ID].Protect()) + if (propagateProctected && IsCriticalDep && toUpgrade.size() == 1) { if (not MarkInstall(InstPkg, false, Depth + 1, false, ForceImportantDeps)) continue; @@ -1303,9 +1303,9 @@ bool pkgDepCache::MarkInstall_InstallDependencies(PkgIterator const &Pkg, unsign } if (not foundSolution && IsCriticalDep) { - StateCache &State = PkgState[Pkg->ID]; - if (not State.Protect()) + if (not propagateProctected) { + StateCache &State = PkgState[Pkg->ID]; RemoveSizes(Pkg); RemoveStates(Pkg); if (Pkg->CurrentVer != 0) @@ -1380,7 +1380,26 @@ bool pkgDepCache::MarkInstall(PkgIterator const &Pkg, bool AutoInst, if (DebugMarker) std::clog << OutputInDepth(Depth) << "MarkInstall " << APT::PrettyPkg(this, Pkg) << " FU=" << FromUser << '\n'; - if (not MarkInstall_UpgradeOrRemoveConflicts(Pkg, Depth, ForceImportantDeps, toUpgrade)) + class ScopedProtected + { + pkgDepCache::StateCache &P; + bool const already; + + public: + ScopedProtected(pkgDepCache::StateCache &p) : P{p}, already{P.Protect()} + { + if (not already) + P.iFlags |= Protected; + } + ~ScopedProtected() + { + if (not already) + P.iFlags &= (~Protected); + } + operator bool() noexcept { return already; } + } propagateProctected{PkgState[Pkg->ID]}; + + if (not MarkInstall_UpgradeOrRemoveConflicts(propagateProctected, Depth, ForceImportantDeps, toUpgrade)) return false; bool const MoveAutoBitToDependencies = [&]() { @@ -1400,7 +1419,7 @@ bool pkgDepCache::MarkInstall(PkgIterator const &Pkg, bool AutoInst, }(); APT::PackageVector toMoveAuto; - if (not MarkInstall_InstallDependencies(Pkg, Depth, ForceImportantDeps, toInstall, MoveAutoBitToDependencies ? &toMoveAuto : nullptr)) + if (not MarkInstall_InstallDependencies(Pkg, Depth, ForceImportantDeps, toInstall, MoveAutoBitToDependencies ? &toMoveAuto : nullptr, propagateProctected)) return false; if (MoveAutoBitToDependencies) -- cgit v1.2.3