#include <apt-pkg/pkgsystem.h>
#include <apt-pkg/tagfile.h>
#include <apt-pkg/progress.h>
+#include <apt-pkg/cacheset.h>
+#include <algorithm>
#include <iostream>
#include <sstream>
#include <set>
/* Check simple depends. A depends -should- never self match but
we allow it anyhow because dpkg does. Technically it is a packaging
bug. Conflicts may never self match */
- if (Dep.TargetPkg() != Dep.ParentPkg() || Dep.IsNegative() == false)
+ if (Dep.IsIgnorable(Res) == false)
{
PkgIterator Pkg = Dep.TargetPkg();
// Check the base package
if (Type == NowVersion && Pkg->CurrentVer != 0)
- if (VS().CheckDep(Pkg.CurrentVer().VerStr(),Dep->CompareOp,
- Dep.TargetVer()) == true)
+ if (Dep.IsSatisfied(Pkg.CurrentVer()) == true)
return true;
if (Type == InstallVersion && PkgState[Pkg->ID].InstallVer != 0)
- if (VS().CheckDep(PkgState[Pkg->ID].InstVerIter(*this).VerStr(),
- Dep->CompareOp,Dep.TargetVer()) == true)
+ if (Dep.IsSatisfied(PkgState[Pkg->ID].InstVerIter(*this)) == true)
return true;
if (Type == CandidateVersion && PkgState[Pkg->ID].CandidateVer != 0)
- if (VS().CheckDep(PkgState[Pkg->ID].CandidateVerIter(*this).VerStr(),
- Dep->CompareOp,Dep.TargetVer()) == true)
+ if (Dep.IsSatisfied(PkgState[Pkg->ID].CandidateVerIter(*this)) == true)
return true;
}
// Check the providing packages
PrvIterator P = Dep.TargetPkg().ProvidesList();
- PkgIterator Pkg = Dep.ParentPkg();
for (; P.end() != true; ++P)
{
- /* Provides may never be applied against the same package (or group)
- if it is a conflicts. See the comment above. */
- if (P.OwnerPkg()->Group == Pkg->Group && Dep.IsNegative() == true)
+ if (Dep.IsIgnorable(P) == true)
continue;
-
+
// Check if the provides is a hit
if (Type == NowVersion)
{
}
// Compare the versions.
- if (VS().CheckDep(P.ProvideVersion(),Dep->CompareOp,Dep.TargetVer()) == true)
+ if (Dep.IsSatisfied(P) == true)
{
Res = P.OwnerPkg();
return true;
dpkg holds are enforced by the private IsModeChangeOk */
bool pkgDepCache::IsDeleteOk(PkgIterator const &Pkg,bool rPurge,
unsigned long Depth, bool FromUser)
+{
+ return IsDeleteOkProtectInstallRequests(Pkg, rPurge, Depth, FromUser);
+}
+bool pkgDepCache::IsDeleteOkProtectInstallRequests(PkgIterator const &Pkg,
+ bool const rPurge, unsigned long const Depth, bool const FromUser)
{
if (FromUser == false && Pkg->CurrentVer == 0)
{
return true;
StateCache &P = PkgState[Pkg->ID];
+ // not changing the mode is obviously also fine as we might want to call
+ // e.g. MarkInstall multiple times with different arguments for the same package
+ if (P.Mode == mode)
+ return true;
// if previous state was set by user only user can reset it
if ((P.iFlags & Protected) == Protected)
{
- if (unlikely(DebugMarker == true) && P.Mode != mode)
+ if (unlikely(DebugMarker == true))
std::clog << OutputInDepth(Depth) << "Ignore Mark" << PrintMode(mode)
<< " of " << Pkg << " as its mode (" << PrintMode(P.Mode)
<< ") is protected" << std::endl;
else if (mode != ModeKeep && Pkg->SelectedState == pkgCache::State::Hold &&
_config->FindB("APT::Ignore-Hold",false) == false)
{
- if (unlikely(DebugMarker == true) && P.Mode != mode)
+ if (unlikely(DebugMarker == true))
std::clog << OutputInDepth(Depth) << "Hold prevents Mark" << PrintMode(mode)
<< " of " << Pkg << std::endl;
return false;
// DepCache::MarkInstall - Put the package in the install state /*{{{*/
// ---------------------------------------------------------------------
/* */
+struct CompareProviders {
+ pkgCache::PkgIterator const Pkg;
+ CompareProviders(pkgCache::DepIterator const &Dep) : Pkg(Dep.TargetPkg()) {};
+ //bool operator() (APT::VersionList::iterator const &AV, APT::VersionList::iterator const &BV)
+ bool operator() (pkgCache::VerIterator const &AV, pkgCache::VerIterator const &BV)
+ {
+ pkgCache::PkgIterator const A = AV.ParentPkg();
+ pkgCache::PkgIterator const B = BV.ParentPkg();
+ // Prefer MA:same packages if other architectures for it are installed
+ if ((AV->MultiArch & pkgCache::Version::Same) == pkgCache::Version::Same ||
+ (BV->MultiArch & pkgCache::Version::Same) == pkgCache::Version::Same)
+ {
+ bool instA = false;
+ if ((AV->MultiArch & pkgCache::Version::Same) == pkgCache::Version::Same)
+ {
+ pkgCache::GrpIterator Grp = A.Group();
+ for (pkgCache::PkgIterator P = Grp.PackageList(); P.end() == false; P = Grp.NextPkg(P))
+ if (P->CurrentVer != 0)
+ {
+ instA = true;
+ break;
+ }
+ }
+ bool instB = false;
+ if ((BV->MultiArch & pkgCache::Version::Same) == pkgCache::Version::Same)
+ {
+ pkgCache::GrpIterator Grp = B.Group();
+ for (pkgCache::PkgIterator P = Grp.PackageList(); P.end() == false; P = Grp.NextPkg(P))
+ {
+ if (P->CurrentVer != 0)
+ {
+ instB = true;
+ break;
+ }
+ }
+ }
+ if (instA != instB)
+ return instA == false;
+ }
+ // 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;
+ }
+ // 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;
+ }
+ 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;
+ }
+ // 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;
+ std::vector<std::string> archs = APT::Configuration::getArchitectures();
+ for (std::vector<std::string>::const_iterator a = archs.begin(); a != archs.end(); ++a)
+ if (*a == A.Arch())
+ return false;
+ else if (*a == B.Arch())
+ return true;
+ }
+ // higher priority seems like a good idea
+ if (AV->Priority != BV->Priority)
+ return AV->Priority > BV->Priority;
+ // unable to decideā¦
+ return A->ID < B->ID;
+ }
+};
bool pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst,
unsigned long Depth, bool FromUser,
bool ForceImportantDeps)
return true;
}
- // check if we are allowed to install the package
- if (IsInstallOk(Pkg,AutoInst,Depth,FromUser) == false)
- return false;
+ // check if we are allowed to install the package (if we haven't already)
+ if (P.Mode != ModeInstall || P.InstallVer != P.CandidateVer)
+ if (IsInstallOk(Pkg,AutoInst,Depth,FromUser) == false)
+ return false;
ActionGroup group(*this);
P.iFlags &= ~AutoKept;
}
/* This bit is for processing the possibilty of an install/upgrade
- fixing the problem */
- SPtrArray<Version *> List = Start.AllTargets();
- if (Start->Type != Dep::DpkgBreaks &&
- (DepState[Start->ID] & DepCVer) == DepCVer)
+ fixing the problem for "positive" dependencies */
+ if (Start.IsNegative() == false && (DepState[Start->ID] & DepCVer) == DepCVer)
{
- // Right, find the best version to install..
- Version **Cur = List;
- PkgIterator P = Start.TargetPkg();
- PkgIterator InstPkg(*Cache,0);
-
- // See if there are direct matches (at the start of the list)
- for (; *Cur != 0 && (*Cur)->ParentPkg == P.Index(); Cur++)
+ APT::VersionList verlist;
+ pkgCache::VerIterator Cand = PkgState[Start.TargetPkg()->ID].CandidateVerIter(*this);
+ if (Cand.end() == false && Start.IsSatisfied(Cand) == true)
+ verlist.insert(Cand);
+ for (PrvIterator Prv = Start.TargetPkg().ProvidesList(); Prv.end() != true; ++Prv)
{
- PkgIterator Pkg(*Cache,Cache->PkgP + (*Cur)->ParentPkg);
- if (PkgState[Pkg->ID].CandidateVer != *Cur)
+ pkgCache::VerIterator V = Prv.OwnerVer();
+ pkgCache::VerIterator Cand = PkgState[Prv.OwnerPkg()->ID].CandidateVerIter(*this);
+ if (Cand.end() == true || V != Cand || Start.IsSatisfied(Prv) == false)
continue;
- InstPkg = Pkg;
- break;
+ verlist.insert(Cand);
}
+ CompareProviders comp(Start);
- // Select the highest priority providing package
- if (InstPkg.end() == true)
- {
- pkgPrioSortList(*Cache,Cur);
- for (; *Cur != 0; Cur++)
- {
- PkgIterator Pkg(*Cache,Cache->PkgP + (*Cur)->ParentPkg);
- if (PkgState[Pkg->ID].CandidateVer != *Cur)
- continue;
- InstPkg = Pkg;
+ do {
+ APT::VersionList::iterator InstVer = std::max_element(verlist.begin(), verlist.end(), comp);
+
+ if (InstVer == verlist.end())
break;
- }
- }
-
- if (InstPkg.end() == false)
- {
+
+ pkgCache::PkgIterator InstPkg = InstVer.ParentPkg();
if(DebugAutoInstall == true)
std::clog << OutputInDepth(Depth) << "Installing " << InstPkg.Name()
<< " as " << Start.DepType() << " of " << Pkg.Name()
<< std::endl;
- // now check if we should consider it a automatic dependency or not
- if(Pkg.Section() && ConfigValueInSubTree("APT::Never-MarkAuto-Sections", Pkg.Section()))
- {
+ if (MarkInstall(InstPkg, true, Depth + 1, false, ForceImportantDeps) == false)
+ {
+ verlist.erase(InstVer);
+ continue;
+ }
+ // now check if we should consider it a automatic dependency or not
+ if(InstPkg->CurrentVer == 0 && Pkg->Section != 0 && ConfigValueInSubTree("APT::Never-MarkAuto-Sections", Pkg.Section()))
+ {
if(DebugAutoInstall == true)
std::clog << OutputInDepth(Depth) << "Setting NOT as auto-installed (direct "
<< Start.DepType() << " of pkg in APT::Never-MarkAuto-Sections)" << std::endl;
- MarkInstall(InstPkg,true,Depth + 1, true);
- }
- else
- {
- // mark automatic dependency
- MarkInstall(InstPkg,true,Depth + 1, false, ForceImportantDeps);
- // Set the autoflag, after MarkInstall because MarkInstall unsets it
- if (P->CurrentVer == 0)
- PkgState[InstPkg->ID].Flags |= Flag::Auto;
- }
- }
+ MarkAuto(InstPkg, false);
+ }
+ break;
+ } while(true);
continue;
}
-
- /* For conflicts we just de-install the package and mark as auto,
- Conflicts may not have or groups. For dpkg's Breaks we try to
- upgrade the package. */
- if (Start.IsNegative() == true)
+ /* 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)
{
+ SPtrArray<Version *> List = Start.AllTargets();
+ pkgCache::PkgIterator TrgPkg = Start.TargetPkg();
for (Version **I = List; *I != 0; I++)
{
VerIterator Ver(*this,*I);
if (PkgState[Pkg->ID].InstallVer == 0)
continue;
- if (PkgState[Pkg->ID].CandidateVer != *I &&
- Start->Type == Dep::DpkgBreaks &&
+ 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 (MarkDelete(Pkg,false,Depth + 1, false) == false)
- break;
+ 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;
- }
+ }
}
return Dep.end() == true;
/*}}}*/
// DepCache::IsInstallOk - check if it is ok to install this package /*{{{*/
// ---------------------------------------------------------------------
-/* The default implementation does nothing.
+/* The default implementation checks if the installation of an M-A:same
+ package would lead us into a version-screw and if so forbids it.
dpkg holds are enforced by the private IsModeChangeOk */
bool pkgDepCache::IsInstallOk(PkgIterator const &Pkg,bool AutoInst,
unsigned long Depth, bool FromUser)
{
+ return IsInstallOkMultiArchSameVersionSynced(Pkg,AutoInst, Depth, FromUser);
+}
+bool pkgDepCache::IsInstallOkMultiArchSameVersionSynced(PkgIterator const &Pkg,
+ bool const AutoInst, unsigned long const Depth, bool const FromUser)
+{
+ if (FromUser == true) // as always: user is always right
+ return true;
+
+ // ignore packages with none-M-A:same candidates
+ VerIterator const CandVer = PkgState[Pkg->ID].CandidateVerIter(*this);
+ if (unlikely(CandVer.end() == true) || CandVer == Pkg.CurrentVer() ||
+ (CandVer->MultiArch & pkgCache::Version::Same) != pkgCache::Version::Same)
+ return true;
+
+ GrpIterator const Grp = Pkg.Group();
+ for (PkgIterator P = Grp.PackageList(); P.end() == false; P = Grp.NextPkg(P))
+ {
+ // not installed or version synced: fine by definition
+ // (simple string-compare as stuff like '1' == '0:1-0' can't happen here)
+ if (P->CurrentVer == 0 || strcmp(Pkg.CandVersion(), P.CandVersion()) == 0)
+ continue;
+ // packages loosing M-A:same can be out-of-sync
+ VerIterator CV = PkgState[P->ID].CandidateVerIter(*this);
+ if (unlikely(CV.end() == true) ||
+ (CV->MultiArch & pkgCache::Version::Same) != pkgCache::Version::Same)
+ continue;
+
+ // not downloadable means the package is obsolete, so allow out-of-sync
+ if (CV.Downloadable() == false)
+ continue;
+
+ PkgState[Pkg->ID].iFlags |= AutoKept;
+ if (unlikely(DebugMarker == true))
+ std::clog << OutputInDepth(Depth) << "Ignore MarkInstall of " << Pkg
+ << " as its M-A:same siblings are not version-synced" << std::endl;
+ return false;
+ }
+
return true;
}
/*}}}*/
if (unlikely(Pkg.end() == true))
return;
+ APT::PackageList pkglist;
+ if (Pkg->CurrentVer != 0 &&
+ (Pkg.CurrentVer()-> MultiArch & pkgCache::Version::Same) == pkgCache::Version::Same)
+ {
+ pkgCache::GrpIterator Grp = Pkg.Group();
+ for (pkgCache::PkgIterator P = Grp.PackageList(); P.end() == false; P = Grp.NextPkg(P))
+ {
+ if (P->CurrentVer != 0)
+ pkglist.insert(P);
+ }
+ }
+ else
+ pkglist.insert(Pkg);
+
ActionGroup group(*this);
- RemoveSizes(Pkg);
- RemoveStates(Pkg);
-
- StateCache &P = PkgState[Pkg->ID];
- if (To == true)
- P.iFlags |= ReInstall;
- else
- P.iFlags &= ~ReInstall;
-
- AddStates(Pkg);
- AddSizes(Pkg);
+ for (APT::PackageList::const_iterator Pkg = pkglist.begin(); Pkg != pkglist.end(); ++Pkg)
+ {
+ RemoveSizes(Pkg);
+ RemoveStates(Pkg);
+
+ StateCache &P = PkgState[Pkg->ID];
+ if (To == true)
+ P.iFlags |= ReInstall;
+ else
+ P.iFlags &= ~ReInstall;
+
+ AddStates(Pkg);
+ AddSizes(Pkg);
+ }
}
/*}}}*/
// DepCache::SetCandidateVersion - Change the candidate version /*{{{*/
if (Cand.end() == true)
continue;
// check if the current candidate is enough for the versioned dependency - and installable?
- if (VS().CheckDep(P.CandVersion(), Start->CompareOp, Start.TargetVer()) == true &&
+ if (Start.IsSatisfied(Cand) == true &&
(VersionState(Cand.DependsList(), DepInstall, DepCandMin, DepCandPolicy) & DepCandMin) == DepCandMin)
{
itsFine = true;
V = Match.Find(D.TargetPkg());
// check if the version from this release could satisfy the dependency
- if (V.end() == true || VS().CheckDep(V.VerStr(), D->CompareOp, D.TargetVer()) == false)
+ if (V.end() == true || D.IsSatisfied(V) == false)
{
if (stillOr == true)
continue;
{
if(!(PkgState[p->ID].Flags & Flag::Auto) ||
(p->Flags & Flag::Essential) ||
+ (p->Flags & Flag::Important) ||
userFunc.InRootSet(p) ||
// be nice even then a required package violates the policy (#583517)
// and do the full mark process also for required packages
for(VerIterator V = d.TargetPkg().VersionList();
!V.end(); ++V)
{
- if(_system->VS->CheckDep(V.VerStr(), d->CompareOp, d.TargetVer()))
+ if(d.IsSatisfied(V))
{
if(debug_autoremove)
{
for(PrvIterator prv=d.TargetPkg().ProvidesList();
!prv.end(); ++prv)
{
- if(_system->VS->CheckDep(prv.ProvideVersion(), d->CompareOp,
- d.TargetVer()))
+ if(d.IsSatisfied(prv))
{
if(debug_autoremove)
{