// -*- mode: cpp; mode: fold -*-
// Description /*{{{*/
-// $Id: depcache.cc,v 1.3 1998/09/07 05:28:32 jgg Exp $
+// $Id: depcache.cc,v 1.25 2001/05/27 05:36:04 jgg Exp $
/* ######################################################################
Dependency Cache - Caches Dependency information.
#pragma implementation "apt-pkg/depcache.h"
#endif
#include <apt-pkg/depcache.h>
-
#include <apt-pkg/version.h>
#include <apt-pkg/error.h>
- /*}}}*/
+#include <apt-pkg/sptr.h>
+#include <apt-pkg/algorithms.h>
+
+#include <apt-pkg/fileutl.h>
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/pkgsystem.h>
+#include <apt-pkg/tagfile.h>
+
+#include <iostream>
+#include <sstream>
+#include <set>
+
+#include <apti18n.h>
+
+pkgDepCache::ActionGroup::ActionGroup(pkgDepCache &cache) :
+ cache(cache), released(false)
+{
+ ++cache.group_level;
+}
+
+void pkgDepCache::ActionGroup::release()
+{
+ if(!released)
+ {
+ if(cache.group_level == 0)
+ std::cerr << "W: Unbalanced action groups, expect badness" << std::endl;
+ else
+ {
+ --cache.group_level;
+
+ if(cache.group_level == 0)
+ cache.MarkAndSweep();
+ }
+
+ released = false;
+ }
+}
+
+pkgDepCache::ActionGroup::~ActionGroup()
+{
+ release();
+}
// DepCache::pkgDepCache - Constructors /*{{{*/
// ---------------------------------------------------------------------
/* */
-pkgDepCache::pkgDepCache(MMap &Map,OpProgress &Prog) :
- pkgCache(Map), PkgState(0), DepState(0)
+pkgDepCache::pkgDepCache(pkgCache *pCache,Policy *Plcy) :
+ group_level(0), Cache(pCache), PkgState(0), DepState(0)
{
- if (_error->PendingError() == false)
- Init(&Prog);
+ delLocalPolicy = 0;
+ LocalPolicy = Plcy;
+ if (LocalPolicy == 0)
+ delLocalPolicy = LocalPolicy = new Policy;
}
/*}}}*/
// DepCache::~pkgDepCache - Destructor /*{{{*/
{
delete [] PkgState;
delete [] DepState;
+ delete delLocalPolicy;
}
/*}}}*/
// DepCache::Init - Generate the initial extra structures. /*{{{*/
/* This allocats the extension buffers and initializes them. */
bool pkgDepCache::Init(OpProgress *Prog)
{
+ // Suppress mark updates during this operation (just in case) and
+ // run a mark operation when Init terminates.
+ ActionGroup actions(*this);
+
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);
-
+
if (Prog != 0)
{
Prog->OverallProgress(0,2*Head().PackageCount,Head().PackageCount,
- "Building Dependancy Tree");
- Prog->SubProgress(Head().PackageCount,"Candidate Versions");
+ _("Building dependency tree"));
+ Prog->SubProgress(Head().PackageCount,_("Candidate versions"));
}
/* Set the current state of everything. In this state all of the
// 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();
Prog->OverallProgress(Head().PackageCount,2*Head().PackageCount,
Head().PackageCount,
- "Building Dependancy Tree");
- Prog->SubProgress(Head().PackageCount,"Dependency Generation");
+ _("Building dependency tree"));
+ Prog->SubProgress(Head().PackageCount,_("Dependency generation"));
}
Update(Prog);
-
+
+ if(Prog != 0)
+ Prog->Done();
+
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)
+
+bool pkgDepCache::readStateFile(OpProgress *Prog)
{
- // 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;
+ FileFd state_file;
+ string state = _config->FindDir("Dir::State") + "extended_states";
+ if(FileExists(state)) {
+ state_file.Open(state, FileFd::ReadOnly);
+ int file_size = state_file.Size();
+ if(Prog != NULL)
+ Prog->OverallProgress(0, file_size, 1,
+ _("Reading state information"));
+
+ pkgTagFile tagfile(&state_file);
+ pkgTagSection section;
+ int amt=0;
+ while(tagfile.Step(section)) {
+ string pkgname = section.FindS("Package");
+ pkgCache::PkgIterator pkg=Cache->FindPkg(pkgname);
+ // Silently ignore unknown packages and packages with no actual
+ // version.
+ if(!pkg.end() && !pkg.VersionList().end()) {
+ short reason = section.FindI("Auto-Installed", 0);
+ if(reason > 0)
+ PkgState[pkg->ID].Flags |= Flag::Auto;
+ if(_config->FindB("Debug::pkgAutoRemove",false))
+ std::cout << "Auto-Installed : " << pkgname << std::endl;
+ amt+=section.size();
+ if(Prog != NULL)
+ Prog->OverallProgress(amt, file_size, 1,
+ _("Reading state information"));
+ }
+ if(Prog != NULL)
+ Prog->OverallProgress(file_size, file_size, 1,
+ _("Reading state information"));
}
-
- return VerIterator(*this,0);
}
- else
- return Pkg.TargetVer();
+
+ return true;
}
- /*}}}*/
-// DepCache::IsImportantDep - True if the dependency is important /*{{{*/
-// ---------------------------------------------------------------------
-/* */
-bool pkgDepCache::IsImportantDep(DepIterator Dep)
+
+bool pkgDepCache::writeStateFile(OpProgress *prog)
{
- return Dep.IsCritical();
+ if(_config->FindB("Debug::pkgAutoRemove",false))
+ std::clog << "pkgDepCache::writeStateFile()" << std::endl;
+
+ FileFd StateFile;
+ string state = _config->FindDir("Dir::State") + "extended_states";
+
+ // if it does not exist, create a empty one
+ if(!FileExists(state))
+ {
+ StateFile.Open(state, FileFd::WriteEmpty);
+ StateFile.Close();
+ }
+
+ // open it
+ if(!StateFile.Open(state, FileFd::ReadOnly))
+ return _error->Error(_("Failed to open StateFile %s"),
+ state.c_str());
+
+ FILE *OutFile;
+ string outfile = state + ".tmp";
+ if((OutFile = fopen(outfile.c_str(),"w")) == NULL)
+ return _error->Error(_("Failed to write temporary StateFile %s"),
+ outfile.c_str());
+
+ // first merge with the existing sections
+ pkgTagFile tagfile(&StateFile);
+ pkgTagSection section;
+ std::set<string> pkgs_seen;
+ const char *nullreorderlist[] = {0};
+ while(tagfile.Step(section)) {
+ string pkgname = section.FindS("Package");
+ // Silently ignore unknown packages and packages with no actual
+ // version.
+ pkgCache::PkgIterator pkg=Cache->FindPkg(pkgname);
+ if(pkg.end() || pkg.VersionList().end())
+ continue;
+ bool oldAuto = section.FindI("Auto-Installed");
+ bool newAuto = (PkgState[pkg->ID].Flags & Flag::Auto);
+ if(_config->FindB("Debug::pkgAutoRemove",false))
+ std::clog << "Update exisiting AutoInstall info: "
+ << pkg.Name() << std::endl;
+ TFRewriteData rewrite[2];
+ rewrite[0].Tag = "Auto-Installed";
+ rewrite[0].Rewrite = newAuto ? "1" : "0";
+ rewrite[0].NewTag = 0;
+ rewrite[1].Tag = 0;
+ TFRewrite(OutFile, section, nullreorderlist, rewrite);
+ fprintf(OutFile,"\n");
+ pkgs_seen.insert(pkgname);
+ }
+
+ // then write the ones we have not seen yet
+ std::ostringstream ostr;
+ for(pkgCache::PkgIterator pkg=Cache->PkgBegin(); !pkg.end(); pkg++) {
+ if(PkgState[pkg->ID].Flags & Flag::Auto) {
+ if (pkgs_seen.find(pkg.Name()) != pkgs_seen.end()) {
+ if(_config->FindB("Debug::pkgAutoRemove",false))
+ std::clog << "Skipping already written " << pkg.Name() << std::endl;
+ continue;
+ }
+ if(_config->FindB("Debug::pkgAutoRemove",false))
+ std::clog << "Writing new AutoInstall: "
+ << pkg.Name() << std::endl;
+ ostr.str(string(""));
+ ostr << "Package: " << pkg.Name()
+ << "\nAuto-Installed: 1\n\n";
+ fprintf(OutFile,ostr.str().c_str());
+ fprintf(OutFile,"\n");
+ }
+ }
+ fclose(OutFile);
+
+ // move the outfile over the real file
+ rename(outfile.c_str(), state.c_str());
+
+ return true;
}
- /*}}}*/
// DepCache::CheckDep - Checks a single dependency /*{{{*/
// ---------------------------------------------------------------------
/* 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)
+ if (Dep.TargetPkg() != Dep.ParentPkg() ||
+ (Dep->Type != Dep::Conflicts && Dep->Type != Dep::Obsoletes))
{
PkgIterator Pkg = Dep.TargetPkg();
// Check the base package
if (Type == NowVersion && Pkg->CurrentVer != 0)
- if (pkgCheckDep(Dep.TargetVer(),
- Pkg.CurrentVer().VerStr(),Dep->CompareOp) == true)
+ if (VS().CheckDep(Pkg.CurrentVer().VerStr(),Dep->CompareOp,
+ Dep.TargetVer()) == true)
return true;
if (Type == InstallVersion && PkgState[Pkg->ID].InstallVer != 0)
- if (pkgCheckDep(Dep.TargetVer(),
- PkgState[Pkg->ID].InstVerIter(*this).VerStr(),
- Dep->CompareOp) == true)
+ if (VS().CheckDep(PkgState[Pkg->ID].InstVerIter(*this).VerStr(),
+ Dep->CompareOp,Dep.TargetVer()) == true)
return true;
if (Type == CandidateVersion && PkgState[Pkg->ID].CandidateVer != 0)
- if (pkgCheckDep(Dep.TargetVer(),
- PkgState[Pkg->ID].CandidateVerIter(*this).VerStr(),
- Dep->CompareOp) == true)
+ if (VS().CheckDep(PkgState[Pkg->ID].CandidateVerIter(*this).VerStr(),
+ Dep->CompareOp,Dep.TargetVer()) == true)
return true;
}
+ if (Dep->Type == Dep::Obsoletes)
+ return false;
+
// Check the providing packages
PrvIterator P = Dep.TargetPkg().ProvidesList();
PkgIterator Pkg = Dep.ParentPkg();
}
// Compare the versions.
- if (pkgCheckDep(Dep.TargetVer(),P.ProvideVersion(),Dep->CompareOp) == true)
+ if (VS().CheckDep(P.ProvideVersion(),Dep->CompareOp,Dep.TargetVer()) == true)
{
Res = P.OwnerPkg();
return true;
// 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)
+void pkgDepCache::AddSizes(const PkgIterator &Pkg,signed long Mult)
{
StateCache &P = PkgState[Pkg->ID];
+ if (Pkg->VersionList == 0)
+ return;
+
+ if (Pkg.State() == pkgCache::PkgIterator::NeedsConfigure &&
+ P.Keep() == true)
+ return;
+
// Compute the size data
if (P.NewInstall() == true)
{
- iUsrSize += Mult*P.InstVerIter(*this)->InstalledSize;
- iDownloadSize += Mult*P.InstVerIter(*this)->Size;
+ iUsrSize += (signed)(Mult*P.InstVerIter(*this)->InstalledSize);
+ iDownloadSize += (signed)(Mult*P.InstVerIter(*this)->Size);
+ return;
}
// Upgrading
- if (Pkg->CurrentVer != 0 && P.InstallVer != (Version *)Pkg.CurrentVer() &&
- P.InstallVer != 0)
+ if (Pkg->CurrentVer != 0 &&
+ (P.InstallVer != (Version *)Pkg.CurrentVer() ||
+ (P.iFlags & ReInstall) == ReInstall) && P.InstallVer != 0)
+ {
+ iUsrSize += (signed)(Mult*((signed)P.InstVerIter(*this)->InstalledSize -
+ (signed)Pkg.CurrentVer()->InstalledSize));
+ iDownloadSize += (signed)(Mult*P.InstVerIter(*this)->Size);
+ return;
+ }
+
+ // Reinstall
+ if (Pkg.State() == pkgCache::PkgIterator::NeedsUnpack &&
+ P.Delete() == false)
{
- iUsrSize += Mult*(P.InstVerIter(*this)->InstalledSize -
- Pkg.CurrentVer()->InstalledSize);
- iDownloadSize += Mult*P.InstVerIter(*this)->Size;
+ iDownloadSize += (signed)(Mult*P.InstVerIter(*this)->Size);
+ return;
}
// Removing
if (Pkg->CurrentVer != 0 && P.InstallVer == 0)
- iUsrSize -= Mult*Pkg.CurrentVer()->InstalledSize;
+ {
+ iUsrSize -= (signed)(Mult*Pkg.CurrentVer()->InstalledSize);
+ return;
+ }
}
/*}}}*/
// DepCache::AddStates - Add the package to the state counter /*{{{*/
// Not installed
if (Pkg->CurrentVer == 0)
{
+ if (State.Mode == ModeDelete &&
+ (State.iFlags | Purge) == Purge && Pkg.Purge() == false)
+ iDelCount += Add;
+
if (State.Mode == ModeInstall)
iInstCount += Add;
return;
}
// Installed, no upgrade
- if (State.Upgradable() == false)
- {
+ if (State.Status == 0)
+ {
if (State.Mode == ModeDelete)
iDelCount += Add;
+ else
+ if ((State.iFlags & ReInstall) == ReInstall)
+ iInstCount += Add;
+
return;
}
/* Invert for Conflicts. We have to do this twice to get the
right sense for a conflicts group */
- if (D->Type == Dep::Conflicts)
+ if (D->Type == Dep::Conflicts || D->Type == Dep::Obsoletes)
State = ~State;
// Add to the group if we are within an or..
+ State &= 0x7;
Group |= State;
State |= Group << 3;
if ((D->CompareOp & Dep::Or) != Dep::Or)
Group = 0;
// Invert for Conflicts
- if (D->Type == Dep::Conflicts)
+ if (D->Type == Dep::Conflicts || D->Type == Dep::Obsoletes)
State = ~State;
}
}
{
// Build the dependency state.
unsigned char &State = DepState[D->ID];
- State = DependencyState(D);;
+ State = DependencyState(D);
// Add to the group if we are within an or..
Group |= State;
Group = 0;
// Invert for Conflicts
- if (D->Type == Dep::Conflicts)
+ if (D->Type == Dep::Conflicts || D->Type == Dep::Obsoletes)
State = ~State;
}
}
if (Prog != 0)
Prog->Progress(Done);
+
+ readStateFile(Prog);
}
/*}}}*/
// 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. */
+ It is mainly meant to scan reverse dependencies. */
void pkgDepCache::Update(DepIterator D)
{
// Update the reverse deps
State = DependencyState(D);
// Invert for Conflicts
- if (D->Type == Dep::Conflicts)
+ if (D->Type == Dep::Conflicts || D->Type == Dep::Obsoletes)
State = ~State;
-
+
RemoveStates(D.ParentPkg());
BuildGroupOrs(D.ParentVer());
UpdateVerState(D.ParentPkg());
/* 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);
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());
+ if (PkgState[Pkg->ID].CandidateVer != 0)
+ 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)
+void pkgDepCache::MarkKeep(PkgIterator const &Pkg, bool Soft, bool FromUser)
{
// Simplifies other routines.
if (Pkg.end() == true)
return;
+
+ /* Reject an attempt to keep a non-source broken installed package, those
+ must be upgraded */
+ 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];
// We dont even try to keep virtual packages..
if (Pkg->VersionList == 0)
return;
-
- P.Flags &= ~Flag::Auto;
+
+#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
+ // - this makes sense as default when all Garbage dependencies
+ // are automatically marked for removal (as aptitude does).
+ // setting a package for keep then makes it no longer autoinstalled
+ // for all other use-case this action is rather suprising
+ if(FromUser && !P.Marked)
+ P.Flags &= ~Flag::Auto;
+#endif
+
RemoveSizes(Pkg);
RemoveStates(Pkg);
// DepCache::MarkDelete - Put the package in the delete state /*{{{*/
// ---------------------------------------------------------------------
/* */
-void pkgDepCache::MarkDelete(PkgIterator const &Pkg)
+void pkgDepCache::MarkDelete(PkgIterator const &Pkg, bool rPurge)
{
// Simplifies other routines.
if (Pkg.end() == true)
return;
+ ActionGroup group(*this);
+
// Check that it is not already marked for delete
StateCache &P = PkgState[Pkg->ID];
- P.iFlags &= ~AutoKept;
- if (P.Mode == ModeDelete || P.InstallVer == 0)
+ 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;
RemoveSizes(Pkg);
RemoveStates(Pkg);
- P.Mode = ModeDelete;
+ if (Pkg->CurrentVer == 0 && (Pkg.Purge() == true || rPurge == false))
+ P.Mode = ModeKeep;
+ else
+ P.Mode = ModeDelete;
P.InstallVer = 0;
- P.Flags &= Flag::Auto;
AddStates(Pkg);
Update(Pkg);
// DepCache::MarkInstall - Put the package in the install state /*{{{*/
// ---------------------------------------------------------------------
/* */
-void pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst)
-{
+void pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst,
+ unsigned long Depth, bool FromUser)
+{
+ if (Depth > 100)
+ return;
+
// Simplifies other routines.
if (Pkg.end() == true)
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.CandidateVer == (Version *)Pkg.CurrentVer()))
{
if (P.CandidateVer == (Version *)Pkg.CurrentVer() && P.InstallVer == 0)
- MarkKeep(Pkg);
+ MarkKeep(Pkg, false, FromUser);
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)
P.Mode = ModeInstall;
P.InstallVer = P.CandidateVer;
- P.Flags &= ~Flag::Auto;
+
+ 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))
+ P.Flags &= ~Flag::Auto;
+ }
+ else
+ {
+ // Set it to auto if this is a new install.
+ if(P.Status == 2)
+ P.Flags |= Flag::Auto;
+ }
if (P.CandidateVer == (Version *)Pkg.CurrentVer())
P.Mode = ModeKeep;
// Grok or groups
DepIterator Start = Dep;
bool Result = true;
- for (bool LastOR = true; Dep.end() == false && LastOR == true; Dep++)
+ unsigned Ors = 0;
+ for (bool LastOR = true; Dep.end() == false && LastOR == true; Dep++,Ors++)
{
LastOR = (Dep->CompareOp & Dep::Or) == Dep::Or;
continue;
if (Pkg->CurrentVer != 0 && Start.IsCritical() == 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++;
- // Now we have to take action...
- PkgIterator P = Start.SmartTargetPkg();
+ /* This bit is for processing the possibilty of an install/upgrade
+ fixing the problem */
+ SPtrArray<Version *> List = Start.AllTargets();
if ((DepState[Start->ID] & DepCVer) == DepCVer)
{
- MarkInstall(P,true);
+ // Right, find the best version to install..
+ Version **Cur = List;
+ PkgIterator P = Start.TargetPkg();
+ PkgIterator InstPkg(*Cache,0);
- // Set the autoflag, after MarkInstall because MarkInstall unsets it
- if (P->CurrentVer == 0)
- PkgState[P->ID].Flags |= Flag::Auto;
+ // See if there are direct matches (at the start of the list)
+ for (; *Cur != 0 && (*Cur)->ParentPkg == P.Index(); Cur++)
+ {
+ PkgIterator Pkg(*Cache,Cache->PkgP + (*Cur)->ParentPkg);
+ if (PkgState[Pkg->ID].CandidateVer != *Cur)
+ continue;
+ InstPkg = Pkg;
+ break;
+ }
+ // 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;
+ break;
+ }
+ }
+
+ if (InstPkg.end() == false)
+ {
+ if(_config->FindB("Debug::pkgDepCache::AutoInstall",false) == true)
+ std::clog << "Installing " << InstPkg.Name()
+ << " as dep of " << Pkg.Name()
+ << std::endl;
+ // now check if we should consider it a automatic dependency or not
+ string sec = _config->Find("APT::Never-MarkAuto-Section","");
+ if(Pkg.Section() && (string(Pkg.Section()) == sec))
+ {
+ if(_config->FindB("Debug::pkgDepCache::AutoInstall",false) == true)
+ std::clog << "Setting NOT as auto-installed because its a direct dep of a package in section " << sec << std::endl;
+ MarkInstall(InstPkg,true,Depth + 1, true);
+ }
+ else
+ {
+ // mark automatic dependency
+ MarkInstall(InstPkg,true,Depth + 1, false);
+ // Set the autoflag, after MarkInstall because MarkInstall unsets it
+ if (P->CurrentVer == 0)
+ PkgState[InstPkg->ID].Flags |= Flag::Auto;
+ }
+ }
continue;
}
- // For conflicts we just de-install the package and mark as auto
- if (Start->Type == Dep::Conflicts)
+ /* For conflicts we just de-install the package and mark as auto,
+ Conflicts may not have or groups */
+ if (Start->Type == Dep::Conflicts || Start->Type == Dep::Obsoletes)
{
- 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 /*{{{*/
+// DepCache::SetReInstall - Set the reinstallation flag /*{{{*/
// ---------------------------------------------------------------------
-/* 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;
+/* */
+void pkgDepCache::SetReInstall(PkgIterator const &Pkg,bool To)
+{
+ ActionGroup group(*this);
- P.Mode = ModeInstall;
- P.InstallVer = P.CandidateVer;
- if (P.CandidateVer == (Version *)I.CurrentVer())
- P.Mode = ModeKeep;
-
- // Okay autoupgrade it.
- Touch[I->ID] |= 1 << 1;
- }
+ RemoveSizes(Pkg);
+ RemoveStates(Pkg);
- Update();
- ResolveConflicts(Touch);
+ StateCache &P = PkgState[Pkg->ID];
+ if (To == true)
+ P.iFlags |= ReInstall;
+ else
+ P.iFlags &= ~ReInstall;
- delete [] Touch;
+ AddStates(Pkg);
+ AddSizes(Pkg);
}
/*}}}*/
-// DepCache::AllUpgrade - Try to upgrade everything /*{{{*/
+// DepCache::SetCandidateVersion - Change the candidate version /*{{{*/
// ---------------------------------------------------------------------
/* */
-void pkgDepCache::AllUpgrade()
+void pkgDepCache::SetCandidateVersion(VerIterator TargetVer)
{
- // 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);
- }
+ ActionGroup group(*this);
+
+ pkgCache::PkgIterator Pkg = TargetVer.ParentPkg();
+ StateCache &P = PkgState[Pkg->ID];
+
+ RemoveSizes(Pkg);
+ RemoveStates(Pkg);
+
+ if (P.CandidateVer == P.InstallVer)
+ P.InstallVer = (Version *)TargetVer;
+ P.CandidateVer = (Version *)TargetVer;
+ P.Update(Pkg,*this);
- Update();
+ AddStates(Pkg);
+ Update(Pkg);
+ AddSizes(Pkg);
+}
- /* 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;
- }
+void pkgDepCache::MarkAuto(const PkgIterator &Pkg, bool Auto)
+{
+ StateCache &state = PkgState[Pkg->ID];
- // Now downgrade everything that is broken
- ResolveConflicts(Touch);
- delete [] Touch;
+ ActionGroup group(*this);
+
+ if(Auto)
+ state.Flags |= Flag::Auto;
+ else
+ state.Flags &= ~Flag::Auto;
}
/*}}}*/
-#endif
-
// StateCache::Update - Compute the various static display things /*{{{*/
// ---------------------------------------------------------------------
/* This is called whenever the Candidate version changes. */
return Ver;
}
/*}}}*/
+
+// Policy::GetCandidateVer - Returns the Candidate install version /*{{{*/
+// ---------------------------------------------------------------------
+/* The default just returns the highest available version that is not
+ a source and automatic. */
+pkgCache::VerIterator pkgDepCache::Policy::GetCandidateVer(PkgIterator 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++)
+ {
+ if (Pkg.CurrentVer() == I)
+ return I;
+
+ 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 (Last.end() == true)
+ Last = I;
+ continue;
+ }
+
+ return I;
+ }
+ }
+
+ return Last;
+}
+ /*}}}*/
+// Policy::IsImportantDep - True if the dependency is important /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+bool pkgDepCache::Policy::IsImportantDep(DepIterator Dep)
+{
+ return Dep.IsCritical();
+}
+ /*}}}*/
+
+pkgDepCache::DefaultRootSetFunc::DefaultRootSetFunc()
+ : constructedSuccessfully(false)
+{
+ Configuration::Item const *Opts;
+ Opts = _config->Tree("APT::NeverAutoRemove");
+ if (Opts != 0 && Opts->Child != 0)
+ {
+ Opts = Opts->Child;
+ for (; Opts != 0; Opts = Opts->Next)
+ {
+ if (Opts->Value.empty() == true)
+ continue;
+
+ regex_t *p = new regex_t;
+ if(regcomp(p,Opts->Value.c_str(),
+ REG_EXTENDED | REG_ICASE | REG_NOSUB) != 0)
+ {
+ regfree(p);
+ delete p;
+ _error->Error("Regex compilation error for APT::NeverAutoRemove");
+ return;
+ }
+
+ rootSetRegexp.push_back(p);
+ }
+ }
+
+ constructedSuccessfully = true;
+}
+
+pkgDepCache::DefaultRootSetFunc::~DefaultRootSetFunc()
+{
+ for(unsigned int i = 0; i < rootSetRegexp.size(); i++)
+ {
+ regfree(rootSetRegexp[i]);
+ delete rootSetRegexp[i];
+ }
+}
+
+
+bool pkgDepCache::DefaultRootSetFunc::InRootSet(const pkgCache::PkgIterator &pkg)
+{
+ for(unsigned int i = 0; i < rootSetRegexp.size(); i++)
+ if (regexec(rootSetRegexp[i], pkg.Name(), 0, 0, 0) == 0)
+ return true;
+
+ return false;
+}
+
+pkgDepCache::InRootSetFunc *pkgDepCache::GetRootSetFunc()
+{
+ DefaultRootSetFunc *f = new DefaultRootSetFunc;
+ if(f->wasConstructedSuccessfully())
+ return f;
+ else
+ {
+ delete f;
+ return NULL;
+ }
+}
+
+bool pkgDepCache::MarkFollowsRecommends()
+{
+ return _config->FindB("APT::AutoRemove::RecommendsImportant", true);
+}
+
+bool pkgDepCache::MarkFollowsSuggests()
+{
+ return _config->FindB("APT::AutoRemove::SuggestsImportant", false);
+}
+
+// the main mark algorithm
+bool pkgDepCache::MarkRequired(InRootSetFunc &userFunc)
+{
+ bool follow_recommends;
+ bool follow_suggests;
+
+ // init the states
+ for(PkgIterator p = PkgBegin(); !p.end(); ++p)
+ {
+ PkgState[p->ID].Marked = false;
+ PkgState[p->ID].Garbage = false;
+
+ // debug output
+ if(_config->FindB("Debug::pkgAutoRemove",false)
+ && PkgState[p->ID].Flags & Flag::Auto)
+ std::clog << "AutoDep: " << p.Name() << std::endl;
+ }
+
+ // init vars
+ follow_recommends = MarkFollowsRecommends();
+ follow_suggests = MarkFollowsSuggests();
+
+
+
+ // do the mark part, this is the core bit of the algorithm
+ for(PkgIterator p = PkgBegin(); !p.end(); ++p)
+ {
+ if(!(PkgState[p->ID].Flags & Flag::Auto) ||
+ (p->Flags & Flag::Essential) ||
+ userFunc.InRootSet(p))
+
+ {
+ // the package is installed (and set to keep)
+ if(PkgState[p->ID].Keep() && !p.CurrentVer().end())
+ MarkPackage(p, p.CurrentVer(),
+ follow_recommends, follow_suggests);
+ // the package is to be installed
+ else if(PkgState[p->ID].Install())
+ MarkPackage(p, PkgState[p->ID].InstVerIter(*this),
+ follow_recommends, follow_suggests);
+ }
+ }
+
+ return true;
+}
+
+// mark a single package in Mark-and-Sweep
+void pkgDepCache::MarkPackage(const pkgCache::PkgIterator &pkg,
+ const pkgCache::VerIterator &ver,
+ bool follow_recommends,
+ bool follow_suggests)
+{
+ pkgDepCache::StateCache &state = PkgState[pkg->ID];
+ VerIterator candver = state.CandidateVerIter(*this);
+ VerIterator instver = state.InstVerIter(*this);
+
+#if 0
+ // 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
+ // removal of a no-longer used dependency. if the pkg is set to
+ // keep again later it will result in broken deps
+ if(state.Delete() && state.RemoveReason = Unused)
+ {
+ if(ver==candver)
+ mark_install(pkg, false, false, NULL);
+ else if(ver==pkg.CurrentVer())
+ MarkKeep(pkg, false, false);
+
+ instver=state.InstVerIter(*this);
+ }
+#endif
+
+ // Ignore versions other than the InstVer, and ignore packages
+ // that are already going to be removed or just left uninstalled.
+ if(!(ver == instver && !instver.end()))
+ return;
+
+ // if we are marked already we are done
+ if(state.Marked)
+ return;
+
+ //std::cout << "Setting Marked for: " << pkg.Name() << std::endl;
+ state.Marked=true;
+
+ if(!ver.end())
+ {
+ for(DepIterator d = ver.DependsList(); !d.end(); ++d)
+ {
+ if(d->Type == Dep::Depends ||
+ d->Type == Dep::PreDepends ||
+ (follow_recommends &&
+ d->Type == Dep::Recommends) ||
+ (follow_suggests &&
+ d->Type == Dep::Suggests))
+ {
+ // Try all versions of this package.
+ for(VerIterator V = d.TargetPkg().VersionList();
+ !V.end(); ++V)
+ {
+ if(_system->VS->CheckDep(V.VerStr(), d->CompareOp, d.TargetVer()))
+ {
+ MarkPackage(V.ParentPkg(), V,
+ follow_recommends, follow_suggests);
+ }
+ }
+ // Now try virtual packages
+ for(PrvIterator prv=d.TargetPkg().ProvidesList();
+ !prv.end(); ++prv)
+ {
+ if(_system->VS->CheckDep(prv.ProvideVersion(), d->CompareOp,
+ d.TargetVer()))
+ {
+ MarkPackage(prv.OwnerPkg(), prv.OwnerVer(),
+ follow_recommends, follow_suggests);
+ }
+ }
+ }
+ }
+ }
+}
+
+bool pkgDepCache::Sweep()
+{
+ // do the sweep
+ for(PkgIterator p=PkgBegin(); !p.end(); ++p)
+ {
+ StateCache &state=PkgState[p->ID];
+
+ // skip required packages
+ if (!p.CurrentVer().end() &&
+ (p.CurrentVer()->Priority == pkgCache::State::Required))
+ continue;
+
+ // if it is not marked and it is installed, it's garbage
+ if(!state.Marked && (!p.CurrentVer().end() || state.Install()))
+ {
+ state.Garbage=true;
+ if(_config->FindB("Debug::pkgAutoRemove",false))
+ std::cout << "Garbage: " << p.Name() << std::endl;
+ }
+ }
+
+ return true;
+}