|
|
@@ -0,0 +1,903 @@
|
|
|
+// -*- mode: cpp; mode: fold -*-
|
|
|
+// Description /*{{{*/
|
|
|
+// $Id: depcache.cc,v 1.1 1998/07/07 04:17:01 jgg Exp $
|
|
|
+/* ######################################################################
|
|
|
+
|
|
|
+ Dependency Cache - Caches Dependency information.
|
|
|
+
|
|
|
+ ##################################################################### */
|
|
|
+ /*}}}*/
|
|
|
+// Include Files /*{{{*/
|
|
|
+#ifdef __GNUG__
|
|
|
+#pragma implementation "pkglib/depcache.h"
|
|
|
+#endif
|
|
|
+#include <pkglib/depcache.h>
|
|
|
+
|
|
|
+#include <pkglib/version.h>
|
|
|
+#include <pkglib/error.h>
|
|
|
+ /*}}}*/
|
|
|
+
|
|
|
+// DepCache::pkgDepCache - Constructors /*{{{*/
|
|
|
+// ---------------------------------------------------------------------
|
|
|
+/* */
|
|
|
+pkgDepCache::pkgDepCache(MMap &Map) :
|
|
|
+ pkgCache(Map), PkgState(0), DepState(0)
|
|
|
+{
|
|
|
+ if (_error->PendingError() == false)
|
|
|
+ Init();
|
|
|
+}
|
|
|
+ /*}}}*/
|
|
|
+// DepCache::~pkgDepCache - Destructor /*{{{*/
|
|
|
+// ---------------------------------------------------------------------
|
|
|
+/* */
|
|
|
+pkgDepCache::~pkgDepCache()
|
|
|
+{
|
|
|
+ delete [] PkgState;
|
|
|
+ delete [] DepState;
|
|
|
+}
|
|
|
+ /*}}}*/
|
|
|
+// DepCache::ReMap - Regenerate the extra data for the new cache /*{{{*/
|
|
|
+// ---------------------------------------------------------------------
|
|
|
+/* pkgCache's constructors call this function, but because the object is not
|
|
|
+ fully constructed at that point it will not result in this function being
|
|
|
+ called but pkgCache::ReMap will be instead.*/
|
|
|
+bool pkgDepCache::ReMap()
|
|
|
+{
|
|
|
+ if (pkgCache::ReMap() == false)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ return Init();
|
|
|
+}
|
|
|
+ /*}}}*/
|
|
|
+// DepCache::Init - Generate the initial extra structures. /*{{{*/
|
|
|
+// ---------------------------------------------------------------------
|
|
|
+/* This allocats the extension buffers and initializes them. */
|
|
|
+bool pkgDepCache::Init()
|
|
|
+{
|
|
|
+ delete [] PkgState;
|
|
|
+ delete [] DepState;
|
|
|
+ PkgState = new StateCache[Head().PackageCount];
|
|
|
+ DepState = new unsigned char[Head().DependsCount];
|
|
|
+ memset(PkgState,0,sizeof(*PkgState)*Head().PackageCount);
|
|
|
+ memset(DepState,0,sizeof(*DepState)*Head().DependsCount);
|
|
|
+
|
|
|
+ /* Set the current state of everything. In this state all of the
|
|
|
+ packages are kept exactly as is. See AllUpgrade */
|
|
|
+ for (PkgIterator I = PkgBegin(); I.end() != true; I++)
|
|
|
+ {
|
|
|
+ // Find the proper cache slot
|
|
|
+ StateCache &State = PkgState[I->ID];
|
|
|
+ State.iFlags = 0;
|
|
|
+
|
|
|
+ // Figure out the install version
|
|
|
+ State.CandidateVer = GetCandidateVer(I);
|
|
|
+ State.InstallVer = I.CurrentVer();
|
|
|
+ State.Mode = ModeKeep;
|
|
|
+
|
|
|
+ State.Update(I,*this);
|
|
|
+ }
|
|
|
+
|
|
|
+ Update();
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+ /*}}}*/
|
|
|
+// DepCache::GetCandidateVer - Returns the Candidate install version /*{{{*/
|
|
|
+// ---------------------------------------------------------------------
|
|
|
+/* The default just returns the target version if it exists or the
|
|
|
+ highest version. */
|
|
|
+pkgDepCache::VerIterator pkgDepCache::GetCandidateVer(PkgIterator Pkg)
|
|
|
+{
|
|
|
+ // Try to use an explicit target
|
|
|
+ if (Pkg->TargetVer == 0)
|
|
|
+ {
|
|
|
+ /* Not source versions cannot be a candidate version unless they
|
|
|
+ are already installed */
|
|
|
+ for (VerIterator I = Pkg.VersionList(); I.end() == false; I++)
|
|
|
+ {
|
|
|
+ if (Pkg.CurrentVer() == I)
|
|
|
+ return I;
|
|
|
+ for (VerFileIterator J = I.FileList(); J.end() == false; J++)
|
|
|
+ if ((J.File()->Flags & Flag::NotSource) == 0)
|
|
|
+ return I;
|
|
|
+ }
|
|
|
+
|
|
|
+ return VerIterator(*this,0);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ return Pkg.TargetVer();
|
|
|
+}
|
|
|
+ /*}}}*/
|
|
|
+// DepCache::IsImportantDep - True if the dependency is important /*{{{*/
|
|
|
+// ---------------------------------------------------------------------
|
|
|
+/* */
|
|
|
+bool pkgDepCache::IsImportantDep(DepIterator Dep)
|
|
|
+{
|
|
|
+ return Dep.IsCritical();
|
|
|
+}
|
|
|
+ /*}}}*/
|
|
|
+
|
|
|
+// DepCache::CheckDep - Checks a single dependency /*{{{*/
|
|
|
+// ---------------------------------------------------------------------
|
|
|
+/* This first checks the dependency against the main target package and
|
|
|
+ then walks along the package provides list and checks if each provides
|
|
|
+ will be installed then checks the provides against the dep. Res will be
|
|
|
+ set to the package which was used to satisfy the dep. */
|
|
|
+bool pkgDepCache::CheckDep(DepIterator Dep,int Type,PkgIterator &Res)
|
|
|
+{
|
|
|
+ Res = Dep.TargetPkg();
|
|
|
+
|
|
|
+ /* 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)
|
|
|
+ {
|
|
|
+ PkgIterator Pkg = Dep.TargetPkg();
|
|
|
+ // Check the base package
|
|
|
+ if (Type == NowVersion && Pkg->CurrentVer != 0)
|
|
|
+ if (pkgCheckDep(Dep.TargetVer(),
|
|
|
+ Pkg.CurrentVer().VerStr(),Dep->CompareOp) == true)
|
|
|
+ return true;
|
|
|
+
|
|
|
+ if (Type == InstallVersion && PkgState[Pkg->ID].InstallVer != 0)
|
|
|
+ if (pkgCheckDep(Dep.TargetVer(),
|
|
|
+ PkgState[Pkg->ID].InstVerIter(*this).VerStr(),
|
|
|
+ Dep->CompareOp) == true)
|
|
|
+ return true;
|
|
|
+
|
|
|
+ if (Type == CandidateVersion && PkgState[Pkg->ID].CandidateVer != 0)
|
|
|
+ if (pkgCheckDep(Dep.TargetVer(),
|
|
|
+ PkgState[Pkg->ID].CandidateVerIter(*this).VerStr(),
|
|
|
+ Dep->CompareOp) == 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 if it is
|
|
|
+ a conflicts. See the comment above. */
|
|
|
+ if (P.OwnerPkg() == Pkg && Dep->Type == Dep::Conflicts)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ // Check if the provides is a hit
|
|
|
+ if (Type == NowVersion)
|
|
|
+ {
|
|
|
+ if (P.OwnerPkg().CurrentVer() != P.OwnerVer())
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (Type == InstallVersion)
|
|
|
+ {
|
|
|
+ StateCache &State = PkgState[P.OwnerPkg()->ID];
|
|
|
+ if (State.InstallVer != (Version *)P.OwnerVer())
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (Type == CandidateVersion)
|
|
|
+ {
|
|
|
+ StateCache &State = PkgState[P.OwnerPkg()->ID];
|
|
|
+ if (State.CandidateVer != (Version *)P.OwnerVer())
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Compare the versions.
|
|
|
+ if (pkgCheckDep(Dep.TargetVer(),P.ProvideVersion(),Dep->CompareOp) == true)
|
|
|
+ {
|
|
|
+ Res = P.OwnerPkg();
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return false;
|
|
|
+}
|
|
|
+ /*}}}*/
|
|
|
+// DepCache::AddSizes - Add the packages sizes to the counters /*{{{*/
|
|
|
+// ---------------------------------------------------------------------
|
|
|
+/* Call with Mult = -1 to preform the inverse opration */
|
|
|
+void pkgDepCache::AddSizes(const PkgIterator &Pkg,long Mult)
|
|
|
+{
|
|
|
+ StateCache &P = PkgState[Pkg->ID];
|
|
|
+
|
|
|
+ // Compute the size data
|
|
|
+ if (P.NewInstall() == true)
|
|
|
+ {
|
|
|
+ iUsrSize += Mult*P.InstVerIter(*this)->InstalledSize;
|
|
|
+ iDownloadSize += Mult*P.InstVerIter(*this)->Size;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Upgrading
|
|
|
+ if (Pkg->CurrentVer != 0 && P.InstallVer != (Version *)Pkg.CurrentVer() &&
|
|
|
+ P.InstallVer != 0)
|
|
|
+ {
|
|
|
+ iUsrSize += Mult*(P.InstVerIter(*this)->InstalledSize -
|
|
|
+ Pkg.CurrentVer()->InstalledSize);
|
|
|
+ iDownloadSize += Mult*P.InstVerIter(*this)->Size;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Removing
|
|
|
+ if (Pkg->CurrentVer != 0 && P.InstallVer == 0)
|
|
|
+ iUsrSize -= Mult*Pkg.CurrentVer()->InstalledSize;
|
|
|
+}
|
|
|
+ /*}}}*/
|
|
|
+// DepCache::AddStates - Add the package to the state counter /*{{{*/
|
|
|
+// ---------------------------------------------------------------------
|
|
|
+/* This routine is tricky to use, you must make sure that it is never
|
|
|
+ called twice for the same package. This means the Remove/Add section
|
|
|
+ should be as short as possible and not encompass any code that will
|
|
|
+ calld Remove/Add itself. Remember, dependencies can be circular so
|
|
|
+ while processing a dep for Pkg it is possible that Add/Remove
|
|
|
+ will be called on Pkg */
|
|
|
+void pkgDepCache::AddStates(const PkgIterator &Pkg,int Add)
|
|
|
+{
|
|
|
+ StateCache &State = PkgState[Pkg->ID];
|
|
|
+
|
|
|
+ // The Package is broken
|
|
|
+ if ((State.DepState & DepInstMin) != DepInstMin)
|
|
|
+ iBrokenCount += Add;
|
|
|
+
|
|
|
+ // Bad state
|
|
|
+ if (Pkg.State() != PkgIterator::NeedsNothing)
|
|
|
+ iBadCount += Add;
|
|
|
+
|
|
|
+ // Not installed
|
|
|
+ if (Pkg->CurrentVer == 0)
|
|
|
+ {
|
|
|
+ if (State.Mode == ModeInstall)
|
|
|
+ iInstCount += Add;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Installed, no upgrade
|
|
|
+ if (State.Upgradable() == false)
|
|
|
+ {
|
|
|
+ if (State.Mode == ModeDelete)
|
|
|
+ iDelCount += Add;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Alll 3 are possible
|
|
|
+ if (State.Mode == ModeDelete)
|
|
|
+ iDelCount += Add;
|
|
|
+ if (State.Mode == ModeKeep)
|
|
|
+ iKeepCount += Add;
|
|
|
+ if (State.Mode == ModeInstall)
|
|
|
+ iInstCount += Add;
|
|
|
+}
|
|
|
+ /*}}}*/
|
|
|
+// DepCache::BuildGroupOrs - Generate the Or group dep data /*{{{*/
|
|
|
+// ---------------------------------------------------------------------
|
|
|
+/* The or group results are stored in the last item of the or group. This
|
|
|
+ allows easy detection of the state of a whole or'd group. */
|
|
|
+void pkgDepCache::BuildGroupOrs(VerIterator const &V)
|
|
|
+{
|
|
|
+ unsigned char Group = 0;
|
|
|
+
|
|
|
+ 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)
|
|
|
+ State = ~State;
|
|
|
+
|
|
|
+ // Add to the group if we are within an or..
|
|
|
+ Group |= State;
|
|
|
+ State |= Group << 3;
|
|
|
+ if ((D->CompareOp & Dep::Or) != Dep::Or)
|
|
|
+ Group = 0;
|
|
|
+
|
|
|
+ // Invert for Conflicts
|
|
|
+ if (D->Type == Dep::Conflicts)
|
|
|
+ State = ~State;
|
|
|
+ }
|
|
|
+}
|
|
|
+ /*}}}*/
|
|
|
+// DepCache::VersionState - Perform a pass over a dependency list /*{{{*/
|
|
|
+// ---------------------------------------------------------------------
|
|
|
+/* This is used to run over a dependency list and determine the dep
|
|
|
+ state of the list, filtering it through both a Min check and a Policy
|
|
|
+ check. The return result will have SetMin/SetPolicy low if a check
|
|
|
+ fails. It uses the DepState cache for it's computations. */
|
|
|
+unsigned char pkgDepCache::VersionState(DepIterator D,unsigned char Check,
|
|
|
+ unsigned char SetMin,
|
|
|
+ unsigned char SetPolicy)
|
|
|
+{
|
|
|
+ unsigned char Dep = 0xFF;
|
|
|
+
|
|
|
+ while (D.end() != true)
|
|
|
+ {
|
|
|
+ // Compute a single dependency element (glob or)
|
|
|
+ DepIterator Start = D;
|
|
|
+ unsigned char State = 0;
|
|
|
+ for (bool LastOR = true; D.end() == false && LastOR == true; D++)
|
|
|
+ {
|
|
|
+ State |= DepState[D->ID];
|
|
|
+ LastOR = (D->CompareOp & Dep::Or) == Dep::Or;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Minimum deps that must be satisfied to have a working package
|
|
|
+ if (Start.IsCritical() == true)
|
|
|
+ if ((State & Check) != Check)
|
|
|
+ Dep &= ~SetMin;
|
|
|
+
|
|
|
+ // Policy deps that must be satisfied to install the package
|
|
|
+ if (IsImportantDep(Start) == true &&
|
|
|
+ (State & Check) != Check)
|
|
|
+ Dep &= ~SetPolicy;
|
|
|
+ }
|
|
|
+
|
|
|
+ return Dep;
|
|
|
+}
|
|
|
+ /*}}}*/
|
|
|
+// DepCache::DependencyState - Compute the 3 results for a dep /*{{{*/
|
|
|
+// ---------------------------------------------------------------------
|
|
|
+/* This is the main dependency computation bit. It computes the 3 main
|
|
|
+ results for a dependencys, Now, Install and Candidate. Callers must
|
|
|
+ invert the result if dealing with conflicts. */
|
|
|
+unsigned char pkgDepCache::DependencyState(DepIterator &D)
|
|
|
+{
|
|
|
+ unsigned char State = 0;
|
|
|
+
|
|
|
+ if (CheckDep(D,NowVersion) == true)
|
|
|
+ State |= DepNow;
|
|
|
+ if (CheckDep(D,InstallVersion) == true)
|
|
|
+ State |= DepInstall;
|
|
|
+ if (CheckDep(D,CandidateVersion) == true)
|
|
|
+ State |= DepCVer;
|
|
|
+
|
|
|
+ return State;
|
|
|
+}
|
|
|
+ /*}}}*/
|
|
|
+// DepCache::UpdateVerState - Compute the Dep member of the state /*{{{*/
|
|
|
+// ---------------------------------------------------------------------
|
|
|
+/* This determines the combined dependency representation of a package
|
|
|
+ for its two states now and install. This is done by using the pre-generated
|
|
|
+ dependency information. */
|
|
|
+void pkgDepCache::UpdateVerState(PkgIterator Pkg)
|
|
|
+{
|
|
|
+ // Empty deps are always true
|
|
|
+ StateCache &State = PkgState[Pkg->ID];
|
|
|
+ State.DepState = 0xFF;
|
|
|
+
|
|
|
+ // Check the Current state
|
|
|
+ if (Pkg->CurrentVer != 0)
|
|
|
+ {
|
|
|
+ DepIterator D = Pkg.CurrentVer().DependsList();
|
|
|
+ State.DepState &= VersionState(D,DepNow,DepNowMin,DepNowPolicy);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Check the candidate state. We do not compare against the whole as
|
|
|
+ a candidate state but check the candidate version against the
|
|
|
+ install states */
|
|
|
+ if (State.CandidateVer != 0)
|
|
|
+ {
|
|
|
+ DepIterator D = State.CandidateVerIter(*this).DependsList();
|
|
|
+ State.DepState &= VersionState(D,DepInstall,DepCandMin,DepCandPolicy);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Check target state which can only be current or installed
|
|
|
+ if (State.InstallVer != 0)
|
|
|
+ {
|
|
|
+ DepIterator D = State.InstVerIter(*this).DependsList();
|
|
|
+ State.DepState &= VersionState(D,DepInstall,DepInstMin,DepInstPolicy);
|
|
|
+ }
|
|
|
+}
|
|
|
+ /*}}}*/
|
|
|
+// DepCache::Update - Figure out all the state information /*{{{*/
|
|
|
+// ---------------------------------------------------------------------
|
|
|
+/* This will figure out the state of all the packages and all the
|
|
|
+ dependencies based on the current policy. */
|
|
|
+void pkgDepCache::Update()
|
|
|
+{
|
|
|
+ iUsrSize = 0;
|
|
|
+ iDownloadSize = 0;
|
|
|
+ iDelCount = 0;
|
|
|
+ iInstCount = 0;
|
|
|
+ iKeepCount = 0;
|
|
|
+ iBrokenCount = 0;
|
|
|
+ iBadCount = 0;
|
|
|
+
|
|
|
+ // Perform the depends pass
|
|
|
+ for (PkgIterator I = PkgBegin(); I.end() != true; I++)
|
|
|
+ {
|
|
|
+ for (VerIterator V = I.VersionList(); V.end() != true; V++)
|
|
|
+ {
|
|
|
+ unsigned char Group = 0;
|
|
|
+
|
|
|
+ for (DepIterator D = V.DependsList(); D.end() != true; D++)
|
|
|
+ {
|
|
|
+ // Build the dependency state.
|
|
|
+ unsigned char &State = DepState[D->ID];
|
|
|
+ State = DependencyState(D);;
|
|
|
+
|
|
|
+ // Add to the group if we are within an or..
|
|
|
+ Group |= State;
|
|
|
+ State |= Group << 3;
|
|
|
+ if ((D->CompareOp & Dep::Or) != Dep::Or)
|
|
|
+ Group = 0;
|
|
|
+
|
|
|
+ // Invert for Conflicts
|
|
|
+ if (D->Type == Dep::Conflicts)
|
|
|
+ State = ~State;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Compute the pacakge dependency state and size additions
|
|
|
+ AddSizes(I);
|
|
|
+ UpdateVerState(I);
|
|
|
+ AddStates(I);
|
|
|
+ }
|
|
|
+}
|
|
|
+ /*}}}*/
|
|
|
+// DepCache::Update - Update the deps list of a package /*{{{*/
|
|
|
+// ---------------------------------------------------------------------
|
|
|
+/* This is a helper for update that only does the dep portion of the scan.
|
|
|
+ It is mainly ment to scan reverse dependencies. */
|
|
|
+void pkgDepCache::Update(DepIterator D)
|
|
|
+{
|
|
|
+ // Update the reverse deps
|
|
|
+ for (;D.end() != true; D++)
|
|
|
+ {
|
|
|
+ unsigned char &State = DepState[D->ID];
|
|
|
+ State = DependencyState(D);
|
|
|
+
|
|
|
+ // Invert for Conflicts
|
|
|
+ if (D->Type == Dep::Conflicts)
|
|
|
+ State = ~State;
|
|
|
+
|
|
|
+ RemoveStates(D.ParentPkg());
|
|
|
+ BuildGroupOrs(D.ParentVer());
|
|
|
+ UpdateVerState(D.ParentPkg());
|
|
|
+ AddStates(D.ParentPkg());
|
|
|
+ }
|
|
|
+}
|
|
|
+ /*}}}*/
|
|
|
+// DepCache::Update - Update the related deps of a package /*{{{*/
|
|
|
+// ---------------------------------------------------------------------
|
|
|
+/* This is called whenever the state of a package changes. It updates
|
|
|
+ all cached dependencies related to this package. */
|
|
|
+void pkgDepCache::Update(PkgIterator const &Pkg)
|
|
|
+{
|
|
|
+ // Recompute the dep of the package
|
|
|
+ RemoveStates(Pkg);
|
|
|
+ UpdateVerState(Pkg);
|
|
|
+ AddStates(Pkg);
|
|
|
+
|
|
|
+ // Update the reverse deps
|
|
|
+ Update(Pkg.RevDependsList());
|
|
|
+
|
|
|
+ // Update the provides map for the current ver
|
|
|
+ if (Pkg->CurrentVer != 0)
|
|
|
+ for (PrvIterator P = Pkg.CurrentVer().ProvidesList();
|
|
|
+ P.end() != true; P++)
|
|
|
+ Update(P.ParentPkg().RevDependsList());
|
|
|
+
|
|
|
+ // Update the provides map for the candidate ver
|
|
|
+ for (PrvIterator P = PkgState[Pkg->ID].CandidateVerIter(*this).ProvidesList();
|
|
|
+ P.end() != true; P++)
|
|
|
+ Update(P.ParentPkg().RevDependsList());
|
|
|
+}
|
|
|
+
|
|
|
+ /*}}}*/
|
|
|
+
|
|
|
+// DepCache::MarkKeep - Put the package in the keep state /*{{{*/
|
|
|
+// ---------------------------------------------------------------------
|
|
|
+/* */
|
|
|
+void pkgDepCache::MarkKeep(PkgIterator const &Pkg,bool Soft)
|
|
|
+{
|
|
|
+ // Simplifies other routines.
|
|
|
+ if (Pkg.end() == true)
|
|
|
+ return;
|
|
|
+
|
|
|
+ /* We changed the soft state all the time so the UI is a bit nicer
|
|
|
+ to use */
|
|
|
+ StateCache &P = PkgState[Pkg->ID];
|
|
|
+ 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;
|
|
|
+
|
|
|
+ P.Flags &= ~Flag::Auto;
|
|
|
+ RemoveSizes(Pkg);
|
|
|
+ RemoveStates(Pkg);
|
|
|
+
|
|
|
+ P.Mode = ModeKeep;
|
|
|
+ if (Pkg->CurrentVer == 0)
|
|
|
+ P.InstallVer = 0;
|
|
|
+ else
|
|
|
+ P.InstallVer = Pkg.CurrentVer();
|
|
|
+
|
|
|
+ AddStates(Pkg);
|
|
|
+
|
|
|
+ Update(Pkg);
|
|
|
+
|
|
|
+ AddSizes(Pkg);
|
|
|
+}
|
|
|
+ /*}}}*/
|
|
|
+// DepCache::MarkDelete - Put the package in the delete state /*{{{*/
|
|
|
+// ---------------------------------------------------------------------
|
|
|
+/* */
|
|
|
+void pkgDepCache::MarkDelete(PkgIterator const &Pkg)
|
|
|
+{
|
|
|
+ // Simplifies other routines.
|
|
|
+ if (Pkg.end() == true)
|
|
|
+ return;
|
|
|
+
|
|
|
+ // Check that it is not already marked for delete
|
|
|
+ StateCache &P = PkgState[Pkg->ID];
|
|
|
+ P.iFlags &= ~AutoKept;
|
|
|
+ if (P.Mode == ModeDelete || P.InstallVer == 0)
|
|
|
+ return;
|
|
|
+
|
|
|
+ // We dont even try to delete virtual packages..
|
|
|
+ if (Pkg->VersionList == 0)
|
|
|
+ return;
|
|
|
+
|
|
|
+ RemoveSizes(Pkg);
|
|
|
+ RemoveStates(Pkg);
|
|
|
+
|
|
|
+ P.Mode = ModeDelete;
|
|
|
+ P.InstallVer = 0;
|
|
|
+ P.Flags &= Flag::Auto;
|
|
|
+
|
|
|
+ AddStates(Pkg);
|
|
|
+ Update(Pkg);
|
|
|
+ AddSizes(Pkg);
|
|
|
+}
|
|
|
+ /*}}}*/
|
|
|
+// DepCache::MarkInstall - Put the package in the install state /*{{{*/
|
|
|
+// ---------------------------------------------------------------------
|
|
|
+/* */
|
|
|
+void pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst)
|
|
|
+{
|
|
|
+ // Simplifies other routines.
|
|
|
+ if (Pkg.end() == true)
|
|
|
+ return;
|
|
|
+
|
|
|
+ /* 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.InstBroken() == false && (P.Mode == ModeInstall ||
|
|
|
+ P.CandidateVer == (Version *)Pkg.CurrentVer()))
|
|
|
+ {
|
|
|
+ if (P.CandidateVer == (Version *)Pkg.CurrentVer() && P.InstallVer == 0)
|
|
|
+ MarkKeep(Pkg);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // We dont even try to install virtual packages..
|
|
|
+ if (Pkg->VersionList == 0)
|
|
|
+ return;
|
|
|
+
|
|
|
+ /* 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 */
|
|
|
+ RemoveSizes(Pkg);
|
|
|
+ RemoveStates(Pkg);
|
|
|
+
|
|
|
+ P.Mode = ModeInstall;
|
|
|
+ P.InstallVer = P.CandidateVer;
|
|
|
+ P.Flags &= ~Flag::Auto;
|
|
|
+ if (P.CandidateVer == (Version *)Pkg.CurrentVer())
|
|
|
+ P.Mode = ModeKeep;
|
|
|
+
|
|
|
+ AddStates(Pkg);
|
|
|
+ Update(Pkg);
|
|
|
+ AddSizes(Pkg);
|
|
|
+
|
|
|
+ if (AutoInst == false)
|
|
|
+ return;
|
|
|
+
|
|
|
+ DepIterator Dep = P.InstVerIter(*this).DependsList();
|
|
|
+ for (; Dep.end() != true;)
|
|
|
+ {
|
|
|
+ // Grok or groups
|
|
|
+ DepIterator Start = Dep;
|
|
|
+ bool Result = true;
|
|
|
+ for (bool LastOR = true; Dep.end() == false && LastOR == true; Dep++)
|
|
|
+ {
|
|
|
+ LastOR = (Dep->CompareOp & Dep::Or) == Dep::Or;
|
|
|
+
|
|
|
+ if ((DepState[Dep->ID] & DepInstall) == DepInstall)
|
|
|
+ Result = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 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 worry about critical deps */
|
|
|
+ if (IsImportantDep(Start) == false)
|
|
|
+ continue;
|
|
|
+ if (Pkg->CurrentVer != 0 && Start.IsCritical() == false)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ // Now we have to take action...
|
|
|
+ PkgIterator P = Start.SmartTargetPkg();
|
|
|
+ if ((DepState[Start->ID] & DepCVer) == DepCVer)
|
|
|
+ {
|
|
|
+ MarkInstall(P,true);
|
|
|
+
|
|
|
+ // Set the autoflag, after MarkInstall because MarkInstall unsets it
|
|
|
+ if (P->CurrentVer == 0)
|
|
|
+ PkgState[P->ID].Flags |= Flag::Auto;
|
|
|
+
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // For conflicts we just de-install the package and mark as auto
|
|
|
+ if (Start->Type == Dep::Conflicts)
|
|
|
+ {
|
|
|
+ Version **List = Start.AllTargets();
|
|
|
+ for (Version **I = List; *I != 0; I++)
|
|
|
+ {
|
|
|
+ VerIterator Ver(*this,*I);
|
|
|
+ PkgIterator Pkg = Ver.ParentPkg();
|
|
|
+
|
|
|
+ MarkDelete(Pkg);
|
|
|
+ PkgState[Pkg->ID].Flags |= Flag::Auto;
|
|
|
+ }
|
|
|
+ delete [] List;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+ /*}}}*/
|
|
|
+
|
|
|
+#if 0
|
|
|
+// DepCache::ResolveConflicts - Figure the auto upgrades /*{{{*/
|
|
|
+// ---------------------------------------------------------------------
|
|
|
+/* This routine attempts to resolve conflicts generated by automatic
|
|
|
+ upgrading. It is known as 'Stage 1' but that name isn't as proper anymore.
|
|
|
+
|
|
|
+ It's most important function is during the initial load of APT. The
|
|
|
+ loading code will mark every package for upgrade to it's candidate version
|
|
|
+ and then call this routine. This routine will then 'soft keep' every
|
|
|
+ package that causes conflict, is conflicted, or so on. It is a bit
|
|
|
+ agressive in that it may unselect more packages in some odd cases
|
|
|
+ than are strictly necessary but in the case where no packages were
|
|
|
+ conflicting before it will always produce a system with no packages
|
|
|
+ conflicting after.
|
|
|
+
|
|
|
+ This routine is also used during state changes that require autoupgrade
|
|
|
+ scanning. That is, if a new package is marked for install then all packages
|
|
|
+ that have been soft kept are reconsidered for upgrade.
|
|
|
+
|
|
|
+ It is called with state information about what can be un-upgraded and
|
|
|
+ what the pre-upgrade install state was. It is expected that the caller
|
|
|
+ has already marked the desired packages to the install state. Bit 0 is
|
|
|
+ the original install state and Bit 1 is controls whether the package
|
|
|
+ should be touched.
|
|
|
+*/
|
|
|
+void pkgDepCache::ResolveConflicts(unsigned char *Touched)
|
|
|
+{
|
|
|
+ bool Again = false;
|
|
|
+ do
|
|
|
+ {
|
|
|
+ Again = false;
|
|
|
+ for (PkgIterator I = PkgBegin(); I.end() != true; I++)
|
|
|
+ {
|
|
|
+ // The package will install OK
|
|
|
+ if ((PkgState[I->ID].DepState & DepInstMin) == DepInstMin)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ /* The package was broken before and this upgrade will not
|
|
|
+ make things better. We simply mark the package for keep
|
|
|
+ and assume an upgrade attempt will be hopeless. This might
|
|
|
+ not be ideal. */
|
|
|
+ if ((Touched[I->ID] & (1 << 0)) != (1 << 0))
|
|
|
+ {
|
|
|
+ // The package isnt to be touched
|
|
|
+ if ((Touched[I->ID] & (1 << 1)) == (1 << 1))
|
|
|
+ MarkKeep(I,true);
|
|
|
+
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Check to see if not upgrading it will solve the problem
|
|
|
+ if (I->CurrentVer != 0)
|
|
|
+ {
|
|
|
+ // The package isnt to be touched
|
|
|
+ if ((Touched[I->ID] & (1 << 1)) == (1 << 1))
|
|
|
+ {
|
|
|
+ if (PkgState[I->ID].Mode != ModeKeep)
|
|
|
+ Again = true;
|
|
|
+
|
|
|
+ MarkKeep(I,true);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Check if the package is sill broken. If so then we cant leave
|
|
|
+ it as is and get a working system. Lets try marking some
|
|
|
+ depends for 'keep'. This is brutal, it keeps everything in
|
|
|
+ sight to fix the problem. */
|
|
|
+ DepIterator D = I.CurrentVer().DependsList();
|
|
|
+ for (;(PkgState[I->ID].DepState & DepInstMin) != DepInstMin &&
|
|
|
+ D.end() != true; D++)
|
|
|
+ {
|
|
|
+ // We only worry about critical deps.
|
|
|
+ if (D.IsCritical() != true)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ unsigned char State = DepState[D->ID];
|
|
|
+
|
|
|
+ // This dep never was set before so we dont need to set it now
|
|
|
+ if ((State & DepNow) != DepNow)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ // The dep is okay now no worries.
|
|
|
+ if ((State & DepInstall) == DepInstall)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ // Locate a target to keep
|
|
|
+ PkgIterator P(*this);
|
|
|
+ if (CheckDep(D,NowVersion,P) == true)
|
|
|
+ {
|
|
|
+ // We cant touch this package
|
|
|
+ if ((Touched[P->ID] & (1 << 1)) == (1 << 1))
|
|
|
+ MarkKeep(P,true);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ while (Again == true);
|
|
|
+}
|
|
|
+ /*}}}*/
|
|
|
+// DepCache::PromoteAutoKeep - Gentler version of the above /*{{{*/
|
|
|
+// ---------------------------------------------------------------------
|
|
|
+/* This is used when installing packages, all it does is attempt to promote
|
|
|
+ everything that has been auto-kept. It simply promotes everything
|
|
|
+ irregardless of it having a chance to work and then runs ResolveConflicts
|
|
|
+ on the result. This allows circular depends loops to work but isn't
|
|
|
+ terribly fast. */
|
|
|
+void pkgDepCache::PromoteAutoKeep()
|
|
|
+{
|
|
|
+ /* Initialize the touchd array. Bit 0 is the old install state bit 1
|
|
|
+ is the touch value */
|
|
|
+ unsigned char *Touch = new unsigned char[Head().PackageCount];
|
|
|
+ for (unsigned int I = 0; I != Head().PackageCount; I++)
|
|
|
+ {
|
|
|
+ if ((PkgState[I].DepState & DepInstMin) == DepInstMin)
|
|
|
+ Touch[I] = 1 << 0;
|
|
|
+ else
|
|
|
+ Touch[I] = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ // This allows circular depends to work
|
|
|
+ for (PkgIterator I = PkgBegin(); I.end() != true; I++)
|
|
|
+ {
|
|
|
+ /* It wasnt installed before or it is not autokept or it is not
|
|
|
+ upgradeable */
|
|
|
+ StateCache &P = PkgState[I->ID];
|
|
|
+ if (I->CurrentVer == 0 || P.Mode != ModeKeep || I->VersionList == 0 ||
|
|
|
+ P.CandidateVer == (Version *)I.CurrentVer() ||
|
|
|
+ (P.iFlags & AutoKept) != AutoKept)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ P.Mode = ModeInstall;
|
|
|
+ P.InstallVer = P.CandidateVer;
|
|
|
+ if (P.CandidateVer == (Version *)I.CurrentVer())
|
|
|
+ P.Mode = ModeKeep;
|
|
|
+
|
|
|
+ // Okay autoupgrade it.
|
|
|
+ Touch[I->ID] |= 1 << 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ Update();
|
|
|
+ ResolveConflicts(Touch);
|
|
|
+
|
|
|
+ delete [] Touch;
|
|
|
+}
|
|
|
+ /*}}}*/
|
|
|
+// DepCache::AllUpgrade - Try to upgrade everything /*{{{*/
|
|
|
+// ---------------------------------------------------------------------
|
|
|
+/* */
|
|
|
+void pkgDepCache::AllUpgrade()
|
|
|
+{
|
|
|
+ // Set everything to an upgrade mode
|
|
|
+ for (PkgIterator I = PkgBegin(); I.end() != true; I++)
|
|
|
+ {
|
|
|
+ StateCache &State = PkgState[I->ID];
|
|
|
+
|
|
|
+ /* We dont upgrade packages marked for deletion or that are
|
|
|
+ not installed or that don't have an upgrade */
|
|
|
+ if (State.Mode == ModeDelete || I->CurrentVer == 0 ||
|
|
|
+ (Version *)I.CurrentVer() == State.CandidateVer)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ // Set the state to upgrade
|
|
|
+ State.iFlags = 0;
|
|
|
+ State.Mode = ModeInstall;
|
|
|
+ State.InstallVer = State.CandidateVer;
|
|
|
+ if (State.CandidateVer == (Version *)I.CurrentVer())
|
|
|
+ State.Mode = ModeKeep;
|
|
|
+
|
|
|
+ // Do not upgrade things that have the hold flag set.
|
|
|
+ if (I->SelectedState == State::Hold)
|
|
|
+ {
|
|
|
+ State.InstallVer = I.CurrentVer();
|
|
|
+ State.Mode = ModeKeep;
|
|
|
+ }
|
|
|
+ State.Update(I,*this);
|
|
|
+ }
|
|
|
+
|
|
|
+ Update();
|
|
|
+
|
|
|
+ /* Initialize the touchd array. Bit 0 is the old install state bit 1
|
|
|
+ is the touch value */
|
|
|
+ unsigned char *Touch = new unsigned char[Head().PackageCount];
|
|
|
+ for (unsigned int I = 0; I != Head().PackageCount; I++)
|
|
|
+ {
|
|
|
+ if ((PkgState[I].DepState & DepNowMin) == DepNowMin)
|
|
|
+ Touch[I] = (1 << 0) | (1 << 1);
|
|
|
+ else
|
|
|
+ Touch[I] = 1 << 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Now downgrade everything that is broken
|
|
|
+ ResolveConflicts(Touch);
|
|
|
+ delete [] Touch;
|
|
|
+}
|
|
|
+ /*}}}*/
|
|
|
+#endif
|
|
|
+
|
|
|
+// StateCache::Update - Compute the various static display things /*{{{*/
|
|
|
+// ---------------------------------------------------------------------
|
|
|
+/* This is called whenever the Candidate version changes. */
|
|
|
+void pkgDepCache::StateCache::Update(PkgIterator Pkg,pkgCache &Cache)
|
|
|
+{
|
|
|
+ // Some info
|
|
|
+ VerIterator Ver = CandidateVerIter(Cache);
|
|
|
+
|
|
|
+ // Use a null string or the version string
|
|
|
+ if (Ver.end() == true)
|
|
|
+ CandVersion = "";
|
|
|
+ else
|
|
|
+ CandVersion = Ver.VerStr();
|
|
|
+
|
|
|
+ // Find the current version
|
|
|
+ CurVersion = "";
|
|
|
+ if (Pkg->CurrentVer != 0)
|
|
|
+ CurVersion = Pkg.CurrentVer().VerStr();
|
|
|
+
|
|
|
+ // Strip off the epochs for display
|
|
|
+ CurVersion = StripEpoch(CurVersion);
|
|
|
+ CandVersion = StripEpoch(CandVersion);
|
|
|
+
|
|
|
+ // Figure out if its up or down or equal
|
|
|
+ Status = Ver.CompareVer(Pkg.CurrentVer());
|
|
|
+ if (Pkg->CurrentVer == 0 || Pkg->VersionList == 0 || CandidateVer == 0)
|
|
|
+ Status = 2;
|
|
|
+}
|
|
|
+ /*}}}*/
|
|
|
+// StateCache::StripEpoch - Remove the epoch specifier from the version /*{{{*/
|
|
|
+// ---------------------------------------------------------------------
|
|
|
+/* */
|
|
|
+const char *pkgDepCache::StateCache::StripEpoch(const char *Ver)
|
|
|
+{
|
|
|
+ if (Ver == 0)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ // Strip any epoch
|
|
|
+ for (const char *I = Ver; *I != 0; I++)
|
|
|
+ if (*I == ':')
|
|
|
+ return I + 1;
|
|
|
+ return Ver;
|
|
|
+}
|
|
|
+ /*}}}*/
|