// Include Files /*{{{*/
#include <apt-pkg/depcache.h>
#include <apt-pkg/version.h>
+#include <apt-pkg/versionmatch.h>
#include <apt-pkg/error.h>
#include <apt-pkg/sptr.h>
#include <apt-pkg/algorithms.h>
cache.MarkAndSweep();
}
- released = false;
+ released = true;
}
}
/* Set the current state of everything. In this state all of the
packages are kept exactly as is. See AllUpgrade */
int Done = 0;
- for (PkgIterator I = PkgBegin(); I.end() != true; I++,Done++)
+ for (PkgIterator I = PkgBegin(); I.end() != true; ++I, ++Done)
{
if (Prog != 0 && Done%20 == 0)
Prog->Progress(Done);
{
FileFd state_file;
string const state = _config->FindFile("Dir::State::extended_states");
- if(FileExists(state)) {
+ if(RealFileExists(state)) {
state_file.Open(state, FileFd::ReadOnly);
int const file_size = state_file.Size();
if(Prog != NULL)
string const state = _config->FindFile("Dir::State::extended_states");
// if it does not exist, create a empty one
- if(!FileExists(state))
+ if(!RealFileExists(state))
{
- StateFile.Open(state, FileFd::WriteEmpty);
+ StateFile.Open(state, FileFd::WriteAtomic);
StateFile.Close();
}
// then write the ones we have not seen yet
std::ostringstream ostr;
- for(pkgCache::PkgIterator pkg=Cache->PkgBegin(); !pkg.end(); pkg++) {
+ for(pkgCache::PkgIterator pkg=Cache->PkgBegin(); !pkg.end(); ++pkg) {
StateCache const &P = PkgState[pkg->ID];
if(P.Flags & Flag::Auto) {
if (pkgs_seen.find(pkg.FullName()) != pkgs_seen.end()) {
/* 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->Type != Dep::Conflicts && Dep->Type != Dep::DpkgBreaks && Dep->Type != Dep::Obsoletes))
+ if (Dep.TargetPkg() != Dep.ParentPkg() || Dep.IsNegative() == false)
{
PkgIterator Pkg = Dep.TargetPkg();
// Check the base package
// Check the providing packages
PrvIterator P = Dep.TargetPkg().ProvidesList();
PkgIterator Pkg = Dep.ParentPkg();
- for (; P.end() != true; P++)
+ for (; P.end() != true; ++P)
{
- /* Provides may never be applied against the same package if it is
- a conflicts. See the comment above. */
- if (P.OwnerPkg() == Pkg &&
- (Dep->Type == Dep::Conflicts || Dep->Type == Dep::DpkgBreaks))
+ /* 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)
continue;
// Check if the provides is a hit
// Not installed
if (Pkg->CurrentVer == 0)
{
- if (State.Mode == ModeDelete &&
- (State.iFlags | Purge) == Purge && Pkg.Purge() == false)
+ if (State.Mode == ModeDelete &&
+ (State.iFlags & Purge) == Purge && Pkg.Purge() == false)
iDelCount += Add;
if (State.Mode == ModeInstall)
{
unsigned char Group = 0;
- for (DepIterator D = V.DependsList(); D.end() != true; D++)
+ for (DepIterator D = V.DependsList(); D.end() != true; ++D)
{
// Build the dependency state.
unsigned char &State = DepState[D->ID];
/* Invert for Conflicts. We have to do this twice to get the
right sense for a conflicts group */
- if (D->Type == Dep::Conflicts ||
- D->Type == Dep::DpkgBreaks ||
- D->Type == Dep::Obsoletes)
+ if (D.IsNegative() == true)
State = ~State;
// Add to the group if we are within an or..
Group = 0;
// Invert for Conflicts
- if (D->Type == Dep::Conflicts ||
- D->Type == Dep::DpkgBreaks ||
- D->Type == Dep::Obsoletes)
+ if (D.IsNegative() == true)
State = ~State;
}
}
// Compute a single dependency element (glob or)
DepIterator Start = D;
unsigned char State = 0;
- for (bool LastOR = true; D.end() == false && LastOR == true; D++)
+ for (bool LastOR = true; D.end() == false && LastOR == true; ++D)
{
State |= DepState[D->ID];
LastOR = (D->CompareOp & Dep::Or) == Dep::Or;
}
}
/*}}}*/
-// DepCache::RemovePseudoInstalledPkg - MultiArch helper for Update() /*{{{*/
-// ---------------------------------------------------------------------
-/* We "install" arch all packages for all archs if it is installed. Many
- of these will be broken. This method will look at these broken Pkg and
- "remove" it. */
-bool pkgDepCache::RemovePseudoInstalledPkg(PkgIterator &Pkg, std::set<unsigned long> &recheck) {
- if (unlikely(Pkg->CurrentVer == 0))
- return false;
-
- VerIterator V = Pkg.CurrentVer();
- if (V->MultiArch != Version::All)
- return false;
-
- // Never ever kill an "all" package - they have no dependency so they can't be broken
- if (strcmp(Pkg.Arch(),"all") == 0)
- return false;
-
- unsigned char const CurDepState = VersionState(V.DependsList(),DepInstall,DepInstMin,DepInstPolicy);
- if ((CurDepState & DepInstMin) == DepInstMin) {
- // okay, the package isn't broken, but is the package also required?
- // If it has no real dependencies, no installed rdepends and doesn't
- // provide something of value, we will kill it as not required.
- // These pseudopackages have otherwise interesting effects if they get
- // a new dependency in a newer version…
- for (pkgCache::DepIterator D = V.DependsList();
- D.end() != true; ++D)
- if (D.IsCritical() == true && D.ParentPkg()->Group != Pkg->Group)
- return false;
- for (DepIterator D = Pkg.RevDependsList(); D.end() != true; ++D)
- {
- if (D.IsCritical() == false)
- continue;
- PkgIterator const P = D.ParentPkg();
- if (P->Group == Pkg->Group)
- continue;
- if (P->CurrentVer != 0)
- return false;
- }
- for (PrvIterator Prv = V.ProvidesList(); Prv.end() != true; Prv++)
- for (DepIterator d = Prv.ParentPkg().RevDependsList();
- d.end() != true; ++d)
- {
- PkgIterator const P = d.ParentPkg();
- if (P->CurrentVer != 0 &&
- P->Group != Pkg->Group)
- return false;
- }
- }
-
- // Dependencies for this arch all package are not statisfied
- // so we installed it only for our convenience: get right of it now.
- RemoveSizes(Pkg);
- RemoveStates(Pkg);
-
- Pkg->CurrentVer = 0;
- PkgState[Pkg->ID].InstallVer = 0;
-
- AddStates(Pkg);
- Update(Pkg);
- AddSizes(Pkg);
-
- // After the remove previously satisfied pseudo pkg could be now
- // no longer satisfied, so we need to recheck the reverse dependencies
- for (DepIterator d = Pkg.RevDependsList(); d.end() != true; ++d)
- {
- PkgIterator const P = d.ParentPkg();
- if (P->CurrentVer != 0)
- recheck.insert(P.Index());
- }
-
- for (DepIterator d = V.DependsList(); d.end() != true; ++d)
- {
- PkgIterator const P = d.TargetPkg();
- for (PrvIterator Prv = P.ProvidesList(); Prv.end() != true; ++Prv)
- {
- PkgIterator const O = Prv.OwnerPkg();
- if (O->CurrentVer != 0)
- recheck.insert(O.Index());
- }
-
- if (P->CurrentVer != 0)
- recheck.insert(P.Index());
- }
-
- for (PrvIterator Prv = V.ProvidesList(); Prv.end() != true; Prv++)
- {
- for (DepIterator d = Prv.ParentPkg().RevDependsList();
- d.end() != true; ++d)
- {
- PkgIterator const P = d.ParentPkg();
- if (P->CurrentVer == 0)
- continue;
-
- recheck.insert(P.Index());
- }
- }
-
-
- return true;
-}
- /*}}}*/
// DepCache::Update - Figure out all the state information /*{{{*/
// ---------------------------------------------------------------------
/* This will figure out the state of all the packages and all the
iBrokenCount = 0;
iBadCount = 0;
- std::set<unsigned long> recheck;
-
// Perform the depends pass
int Done = 0;
- bool const checkMultiArch = APT::Configuration::getArchitectures().size() > 1;
- unsigned long killed = 0;
- for (PkgIterator I = PkgBegin(); I.end() != true; I++,Done++)
+ for (PkgIterator I = PkgBegin(); I.end() != true; ++I, ++Done)
{
if (Prog != 0 && Done%20 == 0)
Prog->Progress(Done);
- for (VerIterator V = I.VersionList(); V.end() != true; V++)
+ for (VerIterator V = I.VersionList(); V.end() != true; ++V)
{
unsigned char Group = 0;
- for (DepIterator D = V.DependsList(); D.end() != true; D++)
+ for (DepIterator D = V.DependsList(); D.end() != true; ++D)
{
// Build the dependency state.
unsigned char &State = DepState[D->ID];
Group = 0;
// Invert for Conflicts
- if (D->Type == Dep::Conflicts ||
- D->Type == Dep::DpkgBreaks ||
- D->Type == Dep::Obsoletes)
+ if (D.IsNegative() == true)
State = ~State;
}
}
AddSizes(I);
UpdateVerState(I);
AddStates(I);
-
- if (checkMultiArch != true || I->CurrentVer == 0)
- continue;
-
- VerIterator const V = I.CurrentVer();
- if (V->MultiArch != Version::All)
- continue;
-
- recheck.insert(I.Index());
- --Done; // no progress if we need to recheck the package
- }
-
- if (checkMultiArch == true) {
- /* FIXME: recheck breaks proper progress reporting as we don't know
- how many packages we need to recheck. To lower the effect
- a bit we increase with a kill, but we should do something more clever… */
- while(recheck.empty() == false)
- for (std::set<unsigned long>::const_iterator p = recheck.begin();
- p != recheck.end();) {
- if (Prog != 0 && Done%20 == 0)
- Prog->Progress(Done);
- PkgIterator P = PkgIterator(*Cache, Cache->PkgP + *p);
- if (RemovePseudoInstalledPkg(P, recheck) == true) {
- ++killed;
- ++Done;
- }
- recheck.erase(p++);
- }
-
- /* Okay, we have killed a great amount of pseudopackages -
- we have killed so many that we have now arch "all" packages
- without an installed pseudo package, but we NEED an installed
- pseudo package, so we will search now for a pseudo package
- we can install without breaking everything. */
- for (GrpIterator G = Cache->GrpBegin(); G.end() != true; ++G)
- {
- PkgIterator P = G.FindPkg("all");
- if (P.end() == true)
- continue;
- if (P->CurrentVer == 0)
- continue;
- bool installed = false;
- for (P = G.FindPkg("any"); P.end() != true; P = G.NextPkg(P))
- {
- if (strcmp(P.Arch(), "all") == 0)
- continue;
- if (P->CurrentVer == 0)
- continue;
- installed = true;
- break;
- }
- if (installed == false)
- recheck.insert(G.Index());
- }
-
- while (recheck.empty() != true)
- {
- std::set<unsigned long>::const_iterator g = recheck.begin();
- unsigned long const G = *g;
- recheck.erase(g);
- if (unlikely(ReInstallPseudoForGroup(G, recheck) == false))
- _error->Warning(_("Internal error, group '%s' has no installable pseudo package"), GrpIterator(*Cache, Cache->GrpP + G).Name());
- }
}
if (Prog != 0)
readStateFile(Prog);
}
/*}}}*/
-// DepCache::ReInstallPseudoForGroup - MultiArch helper for Update() /*{{{*/
-// ---------------------------------------------------------------------
-/* RemovePseudoInstalledPkg() is very successful. It even kills packages
- to an amount that no pseudo package is left, but we need a pseudo package
- for upgrading senarios so we need to reinstall one pseudopackage which
- doesn't break everything. Thankfully we can't have architecture depending
- negative dependencies so this problem is already eliminated */
-bool pkgDepCache::ReInstallPseudoForGroup(pkgCache::PkgIterator const &P, std::set<unsigned long> &recheck)
-{
- if (P->CurrentVer != 0)
- return true;
- // recursive call for packages which provide this package
- for (pkgCache::PrvIterator Prv = P.ProvidesList(); Prv.end() != true; ++Prv)
- ReInstallPseudoForGroup(Prv.OwnerPkg(), recheck);
- // check if we actually need to look at this group
- unsigned long const G = P->Group;
- std::set<unsigned long>::const_iterator Pi = recheck.find(G);
- if (Pi == recheck.end())
- return true;
- recheck.erase(Pi); // remove here, so we can't fall into an endless loop
- if (unlikely(ReInstallPseudoForGroup(G, recheck) == false))
- {
- recheck.insert(G);
- return false;
- }
- return true;
-}
-bool pkgDepCache::ReInstallPseudoForGroup(unsigned long const &G, std::set<unsigned long> &recheck)
-{
- std::vector<std::string> static const Archs = APT::Configuration::getArchitectures();
- pkgCache::GrpIterator Grp(*Cache, Cache->GrpP + G);
- if (unlikely(Grp.end() == true))
- return false;
- for (std::vector<std::string>::const_iterator a = Archs.begin();
- a != Archs.end(); ++a)
- {
- pkgCache::PkgIterator P = Grp.FindPkg(*a);
- if (P.end() == true)
- continue;
- pkgCache::VerIterator allV = Grp.FindPkg("all").CurrentVer();
- for (VerIterator V = P.VersionList(); V.end() != true; ++V)
- {
- // search for the same version as the all package
- if (allV->Hash != V->Hash || strcmp(allV.VerStr(),V.VerStr()) != 0)
- continue;
- unsigned char const CurDepState = VersionState(V.DependsList(),DepInstall,DepInstMin,DepInstPolicy);
- // If it is broken, try to install dependencies first before retry
- if ((CurDepState & DepInstMin) != DepInstMin)
- {
- for (pkgCache::DepIterator D = V.DependsList(); D.end() != true; ++D)
- {
- if (D->Type != pkgCache::Dep::PreDepends && D->Type != pkgCache::Dep::Depends)
- continue;
- ReInstallPseudoForGroup(D.TargetPkg(), recheck);
- }
- unsigned char const CurDepState = VersionState(V.DependsList(),DepInstall,DepInstMin,DepInstPolicy);
- // if package ist still broken… try another arch
- if ((CurDepState & DepInstMin) != DepInstMin)
- break;
- }
- // dependencies satisfied: reinstall the package
- RemoveSizes(P);
- RemoveStates(P);
- P->CurrentVer = V.Index();
- PkgState[P->ID].InstallVer = V;
- AddStates(P);
- Update(P);
- AddSizes(P);
- return true;
- }
- }
- return false;
-}
- /*}}}*/
// DepCache::Update - Update the deps list of a package /*{{{*/
// ---------------------------------------------------------------------
/* This is a helper for update that only does the dep portion of the scan.
void pkgDepCache::Update(DepIterator D)
{
// Update the reverse deps
- for (;D.end() != true; D++)
+ for (;D.end() != true; ++D)
{
unsigned char &State = DepState[D->ID];
State = DependencyState(D);
// Invert for Conflicts
- if (D->Type == Dep::Conflicts ||
- D->Type == Dep::DpkgBreaks ||
- D->Type == Dep::Obsoletes)
+ if (D.IsNegative() == true)
State = ~State;
RemoveStates(D.ParentPkg());
// Update the provides map for the current ver
if (Pkg->CurrentVer != 0)
for (PrvIterator P = Pkg.CurrentVer().ProvidesList();
- P.end() != true; P++)
+ P.end() != true; ++P)
Update(P.ParentPkg().RevDependsList());
// Update the provides map for the candidate ver
if (PkgState[Pkg->ID].CandidateVer != 0)
for (PrvIterator P = PkgState[Pkg->ID].CandidateVerIter(*this).ProvidesList();
- P.end() != true; P++)
+ P.end() != true; ++P)
Update(P.ParentPkg().RevDependsList());
}
/*}}}*/
void pkgDepCache::MarkKeep(PkgIterator const &Pkg, bool Soft, bool FromUser,
unsigned long Depth)
{
- // Simplifies other routines.
- if (Pkg.end() == true)
+ if (IsModeChangeOk(ModeKeep, Pkg, Depth, FromUser) == false)
return;
/* Reject an attempt to keep a non-source broken installed package, those
if (Pkg.State() == PkgIterator::NeedsUnpack &&
Pkg.CurrentVer().Downloadable() == false)
return;
-
- /** \todo Can this be moved later in the method? */
- ActionGroup group(*this);
/* We changed the soft state all the time so the UI is a bit nicer
to use */
StateCache &P = PkgState[Pkg->ID];
+
+ // Check that it is not already kept
+ if (P.Mode == ModeKeep)
+ return;
+
if (Soft == true)
P.iFlags |= AutoKept;
else
P.iFlags &= ~AutoKept;
-
- // Check that it is not already kept
- if (P.Mode == ModeKeep)
- return;
- // We dont even try to keep virtual packages..
- if (Pkg->VersionList == 0)
- return;
+ ActionGroup group(*this);
+
#if 0 // reseting the autoflag here means we lose the
// auto-mark information if a user selects a package for removal
// but changes his mind then and sets it for keep again
void pkgDepCache::MarkDelete(PkgIterator const &Pkg, bool rPurge,
unsigned long Depth, bool FromUser)
{
- // Simplifies other routines.
- if (Pkg.end() == true)
+ if (IsModeChangeOk(ModeDelete, Pkg, Depth, FromUser) == false)
return;
- ActionGroup group(*this);
+ StateCache &P = PkgState[Pkg->ID];
// Check that it is not already marked for delete
- StateCache &P = PkgState[Pkg->ID];
- P.iFlags &= ~(AutoKept | Purge);
- if (rPurge == true)
- P.iFlags |= Purge;
-
if ((P.Mode == ModeDelete || P.InstallVer == 0) &&
(Pkg.Purge() == true || rPurge == false))
return;
-
- // We dont even try to delete virtual packages..
- if (Pkg->VersionList == 0)
- return;
- // check if we are allowed to install the package
+ // check if we are allowed to remove the package
if (IsDeleteOk(Pkg,rPurge,Depth,FromUser) == false)
return;
+ P.iFlags &= ~(AutoKept | Purge);
+ if (rPurge == true)
+ P.iFlags |= Purge;
+
+ ActionGroup group(*this);
+
if (DebugMarker == true)
- std::clog << OutputInDepth(Depth) << "MarkDelete " << Pkg << " FU=" << FromUser << std::endl;
+ std::clog << OutputInDepth(Depth) << (rPurge ? "MarkPurge " : "MarkDelete ") << Pkg << " FU=" << FromUser << std::endl;
RemoveSizes(Pkg);
RemoveStates(Pkg);
Update(Pkg);
AddSizes(Pkg);
- // if we remove the pseudo package, we also need to remove the "real"
- if (Pkg->CurrentVer != 0 && Pkg.CurrentVer().Pseudo() == true)
- MarkDelete(Pkg.Group().FindPkg("all"), rPurge, Depth+1, FromUser);
}
/*}}}*/
// DepCache::IsDeleteOk - check if it is ok to remove this package /*{{{*/
// ---------------------------------------------------------------------
-/* The default implementation just honors dpkg hold
- But an application using this library can override this method
- to control the MarkDelete behaviour */
+/* The default implementation tries to prevent deletion of install requests.
+ dpkg holds are enforced by the private IsModeChangeOk */
bool pkgDepCache::IsDeleteOk(PkgIterator const &Pkg,bool rPurge,
unsigned long Depth, bool FromUser)
{
- if (FromUser == false && Pkg->SelectedState == pkgCache::State::Hold && _config->FindB("APT::Ignore-Hold",false) == false)
+ if (FromUser == false && Pkg->CurrentVer == 0)
+ {
+ StateCache &P = PkgState[Pkg->ID];
+ if (P.InstallVer != 0 && P.Status == 2 && (P.Flags & Flag::Auto) != Flag::Auto)
+ {
+ if (DebugMarker == true)
+ std::clog << OutputInDepth(Depth) << "Manual install request prevents MarkDelete of " << Pkg << std::endl;
+ return false;
+ }
+ }
+ return true;
+}
+ /*}}}*/
+// DepCache::IsModeChangeOk - check if it is ok to change the mode /*{{{*/
+// ---------------------------------------------------------------------
+/* this is used by all Mark methods on the very first line to check sanity
+ and prevents mode changes for packages on hold for example.
+ If you want to check Mode specific stuff you can use the virtual public
+ Is<Mode>Ok methods instead */
+char const* PrintMode(char const mode)
+{
+ switch (mode)
+ {
+ case pkgDepCache::ModeInstall: return "Install";
+ case pkgDepCache::ModeKeep: return "Keep";
+ case pkgDepCache::ModeDelete: return "Delete";
+ default: return "UNKNOWN";
+ }
+}
+bool pkgDepCache::IsModeChangeOk(ModeList const mode, PkgIterator const &Pkg,
+ unsigned long const Depth, bool const FromUser)
+{
+ // we are not trying to hard…
+ if (unlikely(Depth > 100))
+ return false;
+
+ // general sanity
+ if (unlikely(Pkg.end() == true || Pkg->VersionList == 0))
+ return false;
+
+ // the user is always right
+ if (FromUser == true)
+ return true;
+
+ StateCache &P = PkgState[Pkg->ID];
+
+ // if previous state was set by user only user can reset it
+ if ((P.iFlags & Protected) == Protected)
{
- if (DebugMarker == true)
- std::clog << OutputInDepth(Depth) << "Hold prevents MarkDelete of " << Pkg << " FU=" << FromUser << std::endl;
+ if (unlikely(DebugMarker == true) && P.Mode != mode)
+ std::clog << OutputInDepth(Depth) << "Ignore Mark" << PrintMode(mode)
+ << " of " << Pkg << " as its mode (" << PrintMode(P.Mode)
+ << ") is protected" << std::endl;
return false;
}
+ // enforce dpkg holds
+ else if (mode != ModeKeep && Pkg->SelectedState == pkgCache::State::Hold &&
+ _config->FindB("APT::Ignore-Hold",false) == false)
+ {
+ if (unlikely(DebugMarker == true) && P.Mode != mode)
+ std::clog << OutputInDepth(Depth) << "Hold prevents Mark" << PrintMode(mode)
+ << " of " << Pkg << std::endl;
+ return false;
+ }
+
return true;
}
/*}}}*/
unsigned long Depth, bool FromUser,
bool ForceImportantDeps)
{
- if (Depth > 100)
+ if (IsModeChangeOk(ModeInstall, Pkg, Depth, FromUser) == false)
return;
-
- // Simplifies other routines.
- if (Pkg.end() == true)
+
+ StateCache &P = PkgState[Pkg->ID];
+
+ // See if there is even any possible instalation candidate
+ if (P.CandidateVer == 0)
return;
-
- ActionGroup group(*this);
/* Check that it is not already marked for install and that it can be
installed */
- StateCache &P = PkgState[Pkg->ID];
- P.iFlags &= ~AutoKept;
if ((P.InstPolicyBroken() == false && P.InstBroken() == false) &&
(P.Mode == ModeInstall ||
P.CandidateVer == (Version *)Pkg.CurrentVer()))
return;
}
- // See if there is even any possible instalation candidate
- if (P.CandidateVer == 0)
- return;
- // We dont even try to install virtual packages..
- if (Pkg->VersionList == 0)
- return;
-
// check if we are allowed to install the package
if (IsInstallOk(Pkg,AutoInst,Depth,FromUser) == false)
return;
+ ActionGroup group(*this);
+ P.iFlags &= ~AutoKept;
+
/* Target the candidate version and remove the autoflag. We reset the
autoflag below if this was called recursively. Otherwise the user
should have the ability to de-auto a package by changing its state */
if(FromUser)
{
- // Set it to manual if it's a new install or cancelling the
- // removal of a garbage package.
- if(P.Status == 2 || (!Pkg.CurrentVer().end() && !P.Marked))
+ // Set it to manual if it's a new install or already installed,
+ // but only if its not marked by the autoremover (aptitude depend on this behavior)
+ // or if we do automatic installation (aptitude never does it)
+ if(P.Status == 2 || (Pkg->CurrentVer != 0 && (AutoInst == true || P.Marked == false)))
P.Flags &= ~Flag::Auto;
}
else
Update(Pkg);
AddSizes(Pkg);
- // always trigger the install of the all package for a pseudo package
- if (P.CandidateVerIter(*Cache).Pseudo() == true)
- MarkInstall(Pkg.Group().FindPkg("all"), AutoInst, Depth, FromUser, ForceImportantDeps);
-
if (AutoInst == false)
return;
DepIterator Start = Dep;
bool Result = true;
unsigned Ors = 0;
- for (bool LastOR = true; Dep.end() == false && LastOR == true; Dep++,Ors++)
+ for (bool LastOR = true; Dep.end() == false && LastOR == true; ++Dep, ++Ors)
{
LastOR = (Dep->CompareOp & Dep::Or) == Dep::Or;
*/
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;
+ if (Ors == 1 && (DepState[Start->ID] &DepCVer) != DepCVer && Start.IsNegative() == false)
+ {
+ if(DebugAutoInstall == true)
+ std::clog << OutputInDepth(Depth) << Start << " can't be satisfied!" << std::endl;
+ if (Start.IsCritical() == false)
+ continue;
+ // if the dependency was critical, we can't install it, so remove it again
+ MarkDelete(Pkg,false,Depth + 1, false);
+ return;
+ }
+
/* Check if any ImportantDep() (but not Critical) were added
* since we installed the package. Also check for deps that
* were satisfied in the past: for instance, if a version
* package should follow that Recommends rather than causing the
* dependency to be removed. (bug #470115)
*/
- bool isNewImportantDep = false;
- bool isPreviouslySatisfiedImportantDep = false;
- if(!ForceImportantDeps && !Start.IsCritical())
+ if (Pkg->CurrentVer != 0 && ForceImportantDeps == false && Start.IsCritical() == false)
{
- bool found=false;
- VerIterator instVer = Pkg.CurrentVer();
- if(!instVer.end())
+ bool isNewImportantDep = true;
+ bool isPreviouslySatisfiedImportantDep = false;
+ for (DepIterator D = Pkg.CurrentVer().DependsList(); D.end() != true; ++D)
{
- for (DepIterator D = instVer.DependsList(); D.end() != true; D++)
- {
- //FIXME: deal better with or-groups(?)
- DepIterator LocalStart = D;
-
- if(IsImportantDep(D) && !D.IsCritical() &&
- Start.TargetPkg() == D.TargetPkg())
- {
- if(!isPreviouslySatisfiedImportantDep)
- {
- DepIterator D2 = D;
- while((D2->CompareOp & Dep::Or) != 0)
- ++D2;
-
- isPreviouslySatisfiedImportantDep =
- (((*this)[D2] & DepGNow) != 0);
- }
-
- found=true;
- }
- }
- // this is a new dep if it was not found to be already
- // a important dep of the installed pacakge
- isNewImportantDep = !found;
+ //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 ||
+ Start.TargetPkg() != D.TargetPkg())
+ continue;
+
+ isNewImportantDep = false;
+
+ while ((D->CompareOp & Dep::Or) != 0)
+ ++D;
+
+ isPreviouslySatisfiedImportantDep = (((*this)[D] & DepGNow) != 0);
+ if (isPreviouslySatisfiedImportantDep == true)
+ break;
+ }
+
+ if(isNewImportantDep == true)
+ {
+ if (DebugAutoInstall == true)
+ std::clog << OutputInDepth(Depth) << "new important dependency: "
+ << Start.TargetPkg().FullName() << std::endl;
+ }
+ else if(isPreviouslySatisfiedImportantDep == true)
+ {
+ if (DebugAutoInstall == true)
+ std::clog << OutputInDepth(Depth) << "previously satisfied important dependency on "
+ << Start.TargetPkg().FullName() << std::endl;
+ }
+ else
+ {
+ if (DebugAutoInstall == true)
+ std::clog << OutputInDepth(Depth) << "ignore old unsatisfied important dependency on "
+ << Start.TargetPkg().FullName() << std::endl;
+ continue;
}
}
- if(isNewImportantDep)
- if(DebugAutoInstall == true)
- std::clog << OutputInDepth(Depth) << "new important dependency: "
- << Start.TargetPkg().Name() << std::endl;
- if(isPreviouslySatisfiedImportantDep)
- if(DebugAutoInstall == true)
- std::clog << OutputInDepth(Depth) << "previously satisfied important dependency on "
- << Start.TargetPkg().Name() << std::endl;
-
- // skip important deps if the package is already installed
- if (Pkg->CurrentVer != 0 && Start.IsCritical() == false
- && !isNewImportantDep && !isPreviouslySatisfiedImportantDep
- && !ForceImportantDeps)
- 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++;
/* This bit is for processing the possibilty of an install/upgrade
fixing the problem */
/* 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->Type == Dep::Conflicts || Start->Type == Dep::Obsoletes ||
- Start->Type == Dep::DpkgBreaks)
+ if (Start.IsNegative() == true)
{
for (Version **I = List; *I != 0; I++)
{
VerIterator Ver(*this,*I);
PkgIterator Pkg = Ver.ParentPkg();
- if (Start->Type != Dep::DpkgBreaks)
- MarkDelete(Pkg,false,Depth + 1, false);
- else if (PkgState[Pkg->ID].CandidateVer != *I)
+ /* The List includes all packages providing this dependency,
+ even providers which are not installed, so skip them. */
+ if (PkgState[Pkg->ID].InstallVer == 0)
+ continue;
+
+ if (PkgState[Pkg->ID].CandidateVer != *I &&
+ Start->Type == Dep::DpkgBreaks)
MarkInstall(Pkg,true,Depth + 1, false, ForceImportantDeps);
+ else
+ MarkDelete(Pkg,false,Depth + 1, false);
}
continue;
}
/*}}}*/
// DepCache::IsInstallOk - check if it is ok to install this package /*{{{*/
// ---------------------------------------------------------------------
-/* The default implementation just honors dpkg hold
- But an application using this library can override this method
- to control the MarkInstall behaviour */
+/* The default implementation does nothing.
+ dpkg holds are enforced by the private IsModeChangeOk */
bool pkgDepCache::IsInstallOk(PkgIterator const &Pkg,bool AutoInst,
unsigned long Depth, bool FromUser)
{
- if (FromUser == false && Pkg->SelectedState == pkgCache::State::Hold && _config->FindB("APT::Ignore-Hold",false) == false)
- {
- if (DebugMarker == true)
- std::clog << OutputInDepth(Depth) << "Hold prevents MarkInstall of " << Pkg << " FU=" << FromUser << std::endl;
- return false;
- }
return true;
}
/*}}}*/
AddStates(Pkg);
AddSizes(Pkg);
-
- if (unlikely(Pkg.CurrentVer().end() == true) || Pkg.CurrentVer().Pseudo() == false)
- return;
-
- SetReInstall(Pkg.Group().FindPkg("all"), To);
}
/*}}}*/
// DepCache::SetCandidateVersion - Change the candidate version /*{{{*/
/* */
void pkgDepCache::SetCandidateVersion(VerIterator TargetVer, bool const &Pseudo)
{
- ActionGroup group(*this);
-
pkgCache::PkgIterator Pkg = TargetVer.ParentPkg();
StateCache &P = PkgState[Pkg->ID];
+ if (P.CandidateVer == TargetVer)
+ return;
+
+ ActionGroup group(*this);
+
RemoveSizes(Pkg);
RemoveStates(Pkg);
- if (P.CandidateVer == P.InstallVer)
+ if (P.CandidateVer == P.InstallVer && P.Install() == true)
P.InstallVer = (Version *)TargetVer;
P.CandidateVer = (Version *)TargetVer;
P.Update(Pkg,*this);
Update(Pkg);
AddSizes(Pkg);
- if (TargetVer.Pseudo() == false || Pseudo == false)
- return;
+}
+ /*}}}*/
+// DepCache::SetCandidateRelease - Change the candidate version /*{{{*/
+// ---------------------------------------------------------------------
+/* changes the candidate of a package and walks over all its dependencies
+ to check if it needs to change the candidate of the dependency, too,
+ to reach a installable versionstate */
+bool pkgDepCache::SetCandidateRelease(pkgCache::VerIterator TargetVer,
+ std::string const &TargetRel)
+{
+ std::list<std::pair<pkgCache::VerIterator, pkgCache::VerIterator> > Changed;
+ return SetCandidateRelease(TargetVer, TargetRel, Changed);
+}
+bool pkgDepCache::SetCandidateRelease(pkgCache::VerIterator TargetVer,
+ std::string const &TargetRel,
+ std::list<std::pair<pkgCache::VerIterator, pkgCache::VerIterator> > &Changed)
+{
+ ActionGroup group(*this);
+ SetCandidateVersion(TargetVer);
+
+ if (TargetRel == "installed" || TargetRel == "candidate") // both doesn't make sense in this context
+ return true;
- // the version was pseudo: set all other pseudos also
- pkgCache::GrpIterator Grp = Pkg.Group();
- for (Pkg = Grp.FindPkg("any"); Pkg.end() == false; ++Pkg)
+ pkgVersionMatch Match(TargetRel, pkgVersionMatch::Release);
+ // save the position of the last element we will not undo - if we have to
+ std::list<std::pair<pkgCache::VerIterator, pkgCache::VerIterator> >::iterator newChanged = --(Changed.end());
+
+ for (pkgCache::DepIterator D = TargetVer.DependsList(); D.end() == false; ++D)
{
- StateCache &P = PkgState[Pkg->ID];
- if (TargetVer.SimilarVer(P.CandidateVerIter(*this)) == true ||
- (P.CandidateVerIter(*this).Pseudo() == false &&
- strcmp(Pkg.Arch(), "all") != 0))
+ if (D->Type != pkgCache::Dep::PreDepends && D->Type != pkgCache::Dep::Depends &&
+ ((D->Type != pkgCache::Dep::Recommends && D->Type != pkgCache::Dep::Suggests) ||
+ IsImportantDep(D) == false))
continue;
- for (pkgCache::VerIterator Ver = Pkg.VersionList(); Ver.end() == false; ++Ver)
+ // walk over an or-group and check if we need to do anything
+ // for simpilicity no or-group is handled as a or-group including one dependency
+ pkgCache::DepIterator Start = D;
+ bool itsFine = false;
+ for (bool stillOr = true; stillOr == true; ++Start)
{
- if (TargetVer.SimilarVer(Ver) == false)
+ stillOr = (Start->CompareOp & Dep::Or) == Dep::Or;
+ pkgCache::PkgIterator const P = Start.TargetPkg();
+ // virtual packages can't be a solution
+ if (P.end() == true || (P->ProvidesList == 0 && P->VersionList == 0))
+ continue;
+ pkgCache::VerIterator const Cand = PkgState[P->ID].CandidateVerIter(*this);
+ // no versioned dependency - but is it installable?
+ if (Start.TargetVer() == 0 || Start.TargetVer()[0] == '\0')
+ {
+ // Check if one of the providers is installable
+ if (P->ProvidesList != 0)
+ {
+ pkgCache::PrvIterator Prv = P.ProvidesList();
+ for (; Prv.end() == false; ++Prv)
+ {
+ pkgCache::VerIterator const C = PkgState[Prv.OwnerPkg()->ID].CandidateVerIter(*this);
+ if (C.end() == true || C != Prv.OwnerVer() ||
+ (VersionState(C.DependsList(), DepInstall, DepCandMin, DepCandPolicy) & DepCandMin) != DepCandMin)
+ continue;
+ break;
+ }
+ if (Prv.end() == true)
+ continue;
+ }
+ // no providers, so check if we have an installable candidate version
+ else if (Cand.end() == true ||
+ (VersionState(Cand.DependsList(), DepInstall, DepCandMin, DepCandPolicy) & DepCandMin) != DepCandMin)
+ continue;
+ itsFine = true;
+ break;
+ }
+ if (Cand.end() == true)
continue;
- SetCandidateVersion(Ver, false);
+ // check if the current candidate is enough for the versioned dependency - and installable?
+ if (VS().CheckDep(P.CandVersion(), Start->CompareOp, Start.TargetVer()) == true &&
+ (VersionState(Cand.DependsList(), DepInstall, DepCandMin, DepCandPolicy) & DepCandMin) == DepCandMin)
+ {
+ itsFine = true;
+ break;
+ }
+ }
+
+ if (itsFine == true) {
+ // something in the or-group was fine, skip all other members
+ for (; (D->CompareOp & Dep::Or) == Dep::Or; ++D);
+ continue;
+ }
+
+ // walk again over the or-group and check each if a candidate switch would help
+ itsFine = false;
+ for (bool stillOr = true; stillOr == true; ++D)
+ {
+ stillOr = (D->CompareOp & Dep::Or) == Dep::Or;
+ // changing candidate will not help if the dependency is not versioned
+ if (D.TargetVer() == 0 || D.TargetVer()[0] == '\0')
+ {
+ if (stillOr == true)
+ continue;
+ break;
+ }
+
+ pkgCache::VerIterator V;
+ if (TargetRel == "newest")
+ V = D.TargetPkg().VersionList();
+ else
+ 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 (stillOr == true)
+ continue;
+ break;
+ }
+
+ pkgCache::VerIterator oldCand = PkgState[D.TargetPkg()->ID].CandidateVerIter(*this);
+ if (V == oldCand)
+ {
+ // Do we already touched this Version? If so, their versioned dependencies are okay, no need to check again
+ for (std::list<std::pair<pkgCache::VerIterator, pkgCache::VerIterator> >::const_iterator c = Changed.begin();
+ c != Changed.end(); ++c)
+ {
+ if (c->first->ParentPkg != V->ParentPkg)
+ continue;
+ itsFine = true;
+ break;
+ }
+ }
+
+ if (itsFine == false)
+ {
+ // change the candidate
+ Changed.push_back(make_pair(oldCand, TargetVer));
+ if (SetCandidateRelease(V, TargetRel, Changed) == false)
+ {
+ if (stillOr == false)
+ break;
+ // undo the candidate changing
+ SetCandidateVersion(oldCand);
+ Changed.pop_back();
+ continue;
+ }
+ itsFine = true;
+ }
+
+ // something in the or-group was fine, skip all other members
+ for (; (D->CompareOp & Dep::Or) == Dep::Or; ++D);
break;
}
+
+ if (itsFine == false && (D->Type == pkgCache::Dep::PreDepends || D->Type == pkgCache::Dep::Depends))
+ {
+ // undo all changes which aren't lead to a solution
+ for (std::list<std::pair<pkgCache::VerIterator, pkgCache::VerIterator> >::const_iterator c = ++newChanged;
+ c != Changed.end(); ++c)
+ SetCandidateVersion(c->first);
+ Changed.erase(newChanged, Changed.end());
+ return false;
+ }
}
+ return true;
}
-
+ /*}}}*/
+// DepCache::MarkAuto - set the Auto flag for a package /*{{{*/
+// ---------------------------------------------------------------------
+/* */
void pkgDepCache::MarkAuto(const PkgIterator &Pkg, bool Auto)
{
StateCache &state = PkgState[Pkg->ID];
// ---------------------------------------------------------------------
/* The default just returns the highest available version that is not
a source and automatic. */
-pkgCache::VerIterator pkgDepCache::Policy::GetCandidateVer(PkgIterator Pkg)
+pkgCache::VerIterator pkgDepCache::Policy::GetCandidateVer(PkgIterator const &Pkg)
{
/* Not source/not automatic versions cannot be a candidate version
unless they are already installed */
VerIterator Last(*(pkgCache *)this,0);
- for (VerIterator I = Pkg.VersionList(); I.end() == false; I++)
+ for (VerIterator I = Pkg.VersionList(); I.end() == false; ++I)
{
if (Pkg.CurrentVer() == I)
return I;
- for (VerFileIterator J = I.FileList(); J.end() == false; J++)
+ for (VerFileIterator J = I.FileList(); J.end() == false; ++J)
{
if ((J.File()->Flags & Flag::NotSource) != 0)
continue;
/* Stash the highest version of a not-automatic source, we use it
if there is nothing better */
- if ((J.File()->Flags & Flag::NotAutomatic) != 0)
+ if ((J.File()->Flags & Flag::NotAutomatic) != 0 ||
+ (J.File()->Flags & Flag::ButAutomaticUpgrades) != 0)
{
if (Last.end() == true)
Last = I;
// Policy::IsImportantDep - True if the dependency is important /*{{{*/
// ---------------------------------------------------------------------
/* */
-bool pkgDepCache::Policy::IsImportantDep(DepIterator Dep)
+bool pkgDepCache::Policy::IsImportantDep(DepIterator const &Dep)
{
if(Dep.IsCritical())
return true;
bool pkgDepCache::MarkFollowsSuggests()
{
- return _config->FindB("APT::AutoRemove::SuggestsImportant", false);
+ return _config->FindB("APT::AutoRemove::SuggestsImportant", true);
}
// pkgDepCache::MarkRequired - the main mark algorithm /*{{{*/
return;
VerIterator const currver = pkg.CurrentVer();
- VerIterator const candver = state.CandidateVerIter(*this);
VerIterator const instver = state.InstVerIter(*this);
#if 0
+ VerIterator const candver = state.CandidateVerIter(*this);
+
// If a package was garbage-collected but is now being marked, we
// should re-select it
// For cases when a pkg is set to upgrade and this trigger the
if(ver.end() == true)
return;
- // If the version belongs to a Multi-Arch all package
- // we will mark all others in this Group with this version also
- if (ver->MultiArch == pkgCache::Version::All &&
- strcmp(ver.Arch(true), "all") == 0)
- {
- GrpIterator G = pkg.Group();
- const char* const VerStr = ver.VerStr();
- for (PkgIterator P = G.FindPkg("any");
- P.end() != true; P = G.NextPkg(P))
- {
- for (VerIterator V = P.VersionList();
- V.end() != true; ++V)
- {
- if (ver->Hash != V->Hash ||
- strcmp(VerStr, V.VerStr()) != 0)
- continue;
- MarkPackage(P, V, follow_recommends, follow_suggests);
- break;
- }
- }
- }
-
for(DepIterator d = ver.DependsList(); !d.end(); ++d)
{
if(d->Type == Dep::Depends ||