#include <apt-pkg/progress.h>
#include <apt-pkg/sourcelist.h>
#include <apt-pkg/configuration.h>
+#include <apt-pkg/aptconfiguration.h>
#include <apt-pkg/strutl.h>
#include <apt-pkg/sptr.h>
#include <apt-pkg/pkgsystem.h>
+#include <apt-pkg/macros.h>
#include <apt-pkg/tagfile.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
-#include <system.h>
/*}}}*/
typedef vector<pkgIndexFile *>::iterator FileIterator;
// CacheGenerator::pkgCacheGenerator - Constructor /*{{{*/
// ---------------------------------------------------------------------
-/* We set the diry flag and make sure that is written to the disk */
+/* We set the dirty flag and make sure that is written to the disk */
pkgCacheGenerator::pkgCacheGenerator(DynamicMMap *pMap,OpProgress *Prog) :
Map(*pMap), Cache(pMap,false), Progress(Prog),
FoundFileDeps(0)
unsigned int Counter = 0;
while (List.Step() == true)
{
- // Get a pointer to the package structure
string const PackageName = List.Package();
if (PackageName.empty() == true)
return false;
+ /* As we handle Arch all packages as architecture bounded
+ we add all information to every (simulated) arch package */
+ std::vector<string> genArch;
+ if (List.ArchitectureAll() == true) {
+ genArch = APT::Configuration::getArchitectures();
+ if (genArch.size() != 1)
+ genArch.push_back("all");
+ } else
+ genArch.push_back(List.Architecture());
+
+ for (std::vector<string>::const_iterator arch = genArch.begin();
+ arch != genArch.end(); ++arch)
+ {
+ // Get a pointer to the package structure
pkgCache::PkgIterator Pkg;
- if (NewPackage(Pkg, PackageName, List.Architecture()) == false)
+ if (NewPackage(Pkg, PackageName, *arch) == false)
return _error->Error(_("Error occurred while processing %s (NewPackage)"),PackageName.c_str());
Counter++;
if (Counter % 100 == 0 && Progress != 0)
if ((*LastDesc == 0 && _error->PendingError()) || NewFileDesc(Desc,List) == false)
return _error->Error(_("Error occurred while processing %s (NewFileDesc2)"),PackageName.c_str());
+ }
}
FoundFileDeps |= List.HasFileDeps();
return Description;
}
/*}}}*/
-// ListParser::NewDepends - Create a dependency element /*{{{*/
+// CacheGenerator::FinishCache - do various finish operations /*{{{*/
+// ---------------------------------------------------------------------
+/* This prepares the Cache for delivery */
+bool pkgCacheGenerator::FinishCache(OpProgress &Progress) {
+ // FIXME: add progress reporting for this operation
+ // Do we have different architectures in your groups ?
+ vector<string> archs = APT::Configuration::getArchitectures();
+ if (archs.size() > 1) {
+ // Create Conflicts in between the group
+ for (pkgCache::GrpIterator G = GetCache().GrpBegin(); G.end() != true; G++) {
+ string const PkgName = G.Name();
+ for (pkgCache::PkgIterator P = G.PackageList(); P.end() != true; P = G.NextPkg(P)) {
+ if (strcmp(P.Arch(),"all") == 0)
+ continue;
+ pkgCache::PkgIterator allPkg;
+ for (pkgCache::VerIterator V = P.VersionList(); V.end() != true; V++) {
+ string const Arch = V.Arch(true);
+ map_ptrloc *OldDepLast = NULL;
+ /* MultiArch handling introduces a lot of implicit Dependencies:
+ - MultiArch: same → Co-Installable if they have the same version
+ - Architecture: all → Need to be Co-Installable for internal reasons
+ - All others conflict with all other group members */
+ bool const coInstall = (V->MultiArch == pkgCache::Version::All ||
+ V->MultiArch == pkgCache::Version::Same);
+ if (V->MultiArch == pkgCache::Version::All && allPkg.end() == true)
+ allPkg = G.FindPkg("all");
+ for (vector<string>::const_iterator A = archs.begin(); A != archs.end(); ++A) {
+ if (*A == Arch)
+ continue;
+ /* We allow only one installed arch at the time
+ per group, therefore each group member conflicts
+ with all other group members */
+ pkgCache::PkgIterator D = G.FindPkg(*A);
+ if (D.end() == true)
+ continue;
+ if (coInstall == true) {
+ // Replaces: ${self}:other ( << ${binary:Version})
+ NewDepends(D, V, V.VerStr(),
+ pkgCache::Dep::Less, pkgCache::Dep::Replaces,
+ OldDepLast);
+ // Breaks: ${self}:other (!= ${binary:Version})
+ NewDepends(D, V, V.VerStr(),
+ pkgCache::Dep::Less, pkgCache::Dep::DpkgBreaks,
+ OldDepLast);
+ NewDepends(D, V, V.VerStr(),
+ pkgCache::Dep::Greater, pkgCache::Dep::DpkgBreaks,
+ OldDepLast);
+ if (V->MultiArch == pkgCache::Version::All) {
+ // Depend on ${self}:all which does depend on nothing
+ NewDepends(allPkg, V, V.VerStr(),
+ pkgCache::Dep::Equals, pkgCache::Dep::Depends,
+ OldDepLast);
+ }
+ } else {
+ // Conflicts: ${self}:other
+ NewDepends(D, V, "",
+ pkgCache::Dep::NoOp, pkgCache::Dep::Conflicts,
+ OldDepLast);
+ }
+ }
+ }
+ }
+ }
+ }
+ return true;
+}
+ /*}}}*/
+// CacheGenerator::NewDepends - Create a dependency element /*{{{*/
// ---------------------------------------------------------------------
/* This creates a dependency element in the tree. It is linked to the
version and to the package that it is pointing to. */
-bool pkgCacheGenerator::ListParser::NewDepends(pkgCache::VerIterator Ver,
- const string &PackageName,
- const string &Arch,
- const string &Version,
- unsigned int Op,
- unsigned int Type)
+bool pkgCacheGenerator::NewDepends(pkgCache::PkgIterator &Pkg,
+ pkgCache::VerIterator &Ver,
+ string const &Version,
+ unsigned int const &Op,
+ unsigned int const &Type,
+ map_ptrloc *OldDepLast)
{
- pkgCache &Cache = Owner->Cache;
-
// Get a structure
- unsigned long const Dependency = Owner->Map.Allocate(sizeof(pkgCache::Dependency));
+ unsigned long const Dependency = Map.Allocate(sizeof(pkgCache::Dependency));
if (unlikely(Dependency == 0))
return false;
Dep->Type = Type;
Dep->CompareOp = Op;
Dep->ID = Cache.HeaderP->DependsCount++;
-
- pkgCache::GrpIterator Grp;
- if (unlikely(Owner->NewGroup(Grp, PackageName) == false))
- return false;
-
- // Locate the target package
- pkgCache::PkgIterator Pkg = Grp.FindPkg(Arch);
- if (Pkg.end() == true) {
- if (unlikely(Owner->NewPackage(Pkg, PackageName, Arch) == false))
- return false;
- }
// Probe the reverse dependency list for a version string that matches
if (Version.empty() == false)
if (I->Version != 0 && I.TargetVer() == Version)
Dep->Version = I->Version;*/
if (Dep->Version == 0)
- if (unlikely((Dep->Version = WriteString(Version)) == 0))
+ if (unlikely((Dep->Version = Map.WriteString(Version)) == 0))
return false;
}
-
+
// Link it to the package
Dep->Package = Pkg.Index();
Dep->NextRevDepends = Pkg->RevDepends;
Pkg->RevDepends = Dep.Index();
-
- /* Link it to the version (at the end of the list)
- Caching the old end point speeds up generation substantially */
- if (OldDepVer != Ver)
+
+ // Do we know where to link the Dependency to?
+ if (OldDepLast == NULL)
{
OldDepLast = &Ver->DependsList;
for (pkgCache::DepIterator D = Ver.DependsList(); D.end() == false; D++)
OldDepLast = &D->NextDepends;
- OldDepVer = Ver;
}
- // Is it a file dependency?
- if (unlikely(PackageName[0] == '/'))
- FoundFileDeps = true;
-
Dep->NextDepends = *OldDepLast;
*OldDepLast = Dep.Index();
OldDepLast = &Dep->NextDepends;
return true;
}
/*}}}*/
+// ListParser::NewDepends - Create the environment for a new dependency /*{{{*/
+// ---------------------------------------------------------------------
+/* This creates a Group and the Package to link this dependency to if
+ needed and handles also the caching of the old endpoint */
+bool pkgCacheGenerator::ListParser::NewDepends(pkgCache::VerIterator Ver,
+ const string &PackageName,
+ const string &Arch,
+ const string &Version,
+ unsigned int Op,
+ unsigned int Type)
+{
+ pkgCache::GrpIterator Grp;
+ if (unlikely(Owner->NewGroup(Grp, PackageName) == false))
+ return false;
+
+ // Locate the target package
+ pkgCache::PkgIterator Pkg = Grp.FindPkg(Arch);
+ if (Pkg.end() == true) {
+ if (unlikely(Owner->NewPackage(Pkg, PackageName, Arch) == false))
+ return false;
+ }
+
+ // Is it a file dependency?
+ if (unlikely(PackageName[0] == '/'))
+ FoundFileDeps = true;
+
+ /* Caching the old end point speeds up generation substantially */
+ if (OldDepVer != Ver) {
+ OldDepLast = NULL;
+ OldDepVer = Ver;
+ }
+
+ return Owner->NewDepends(Pkg, Ver, Version, Op, Type, OldDepLast);
+}
+ /*}}}*/
// ListParser::NewProvides - Create a Provides element /*{{{*/
// ---------------------------------------------------------------------
/* */
bool pkgCacheGenerator::ListParser::NewProvides(pkgCache::VerIterator Ver,
- const string &PackageName,
+ const string &PkgName,
+ const string &PkgArch,
const string &Version)
{
pkgCache &Cache = Owner->Cache;
// We do not add self referencing provides
- if (unlikely(Ver.ParentPkg().Name() == PackageName))
+ if (Ver.ParentPkg().Name() == PkgName && PkgArch == Ver.Arch(true))
return true;
// Get a structure
// Locate the target package
pkgCache::PkgIterator Pkg;
- if (unlikely(Owner->NewPackage(Pkg,PackageName,string(Ver.Arch())) == false))
+ if (unlikely(Owner->NewPackage(Pkg,PkgName, PkgArch) == false))
return false;
// Link it to the package
static bool CheckValidity(const string &CacheFile, FileIterator Start,
FileIterator End,MMap **OutMap = 0)
{
+ bool const Debug = _config->FindB("Debug::pkgCacheGen", false);
// No file, certainly invalid
if (CacheFile.empty() == true || FileExists(CacheFile) == false)
+ {
+ if (Debug == true)
+ std::clog << "CacheFile doesn't exist" << std::endl;
return false;
-
+ }
+
// Map it
FileFd CacheF(CacheFile,FileFd::ReadOnly);
SPtr<MMap> Map = new MMap(CacheF,0);
pkgCache Cache(Map);
if (_error->PendingError() == true || Map->Size() == 0)
{
+ if (Debug == true)
+ std::clog << "Errors are pending or Map is empty()" << std::endl;
_error->Discard();
return false;
}
SPtrArray<bool> Visited = new bool[Cache.HeaderP->PackageFileCount];
memset(Visited,0,sizeof(*Visited)*Cache.HeaderP->PackageFileCount);
for (; Start != End; Start++)
- {
+ {
+ if (Debug == true)
+ std::clog << "Checking PkgFile " << (*Start)->Describe() << ": ";
if ((*Start)->HasPackages() == false)
+ {
+ if (Debug == true)
+ std::clog << "Has NO packages" << std::endl;
continue;
+ }
if ((*Start)->Exists() == false)
{
_error->WarningE("stat",_("Couldn't stat source package list %s"),
(*Start)->Describe().c_str());
#endif
+ if (Debug == true)
+ std::clog << "file doesn't exist" << std::endl;
continue;
}
// FindInCache is also expected to do an IMS check.
pkgCache::PkgFileIterator File = (*Start)->FindInCache(Cache);
if (File.end() == true)
+ {
+ if (Debug == true)
+ std::clog << "FindInCache returned end-Pointer" << std::endl;
return false;
+ }
Visited[File->ID] = true;
+ if (Debug == true)
+ std::clog << "with ID " << File->ID << " is valid" << std::endl;
}
for (unsigned I = 0; I != Cache.HeaderP->PackageFileCount; I++)
if (Visited[I] == false)
+ {
+ if (Debug == true)
+ std::clog << "File with ID" << I << " wasn't visited" << std::endl;
return false;
+ }
if (_error->PendingError() == true)
{
+ if (Debug == true)
+ {
+ std::clog << "Validity failed because of pending errors:" << std::endl;
+ _error->DumpErrors();
+ }
_error->Discard();
return false;
}
bool pkgMakeStatusCache(pkgSourceList &List,OpProgress &Progress,
MMap **OutMap,bool AllowMem)
{
- unsigned long MapSize = _config->FindI("APT::Cache-Limit",24*1024*1024);
+ bool const Debug = _config->FindB("Debug::pkgCacheGen", false);
+ unsigned long const MapSize = _config->FindI("APT::Cache-Limit",24*1024*1024);
vector<pkgIndexFile *> Files;
for (vector<metaIndex *>::const_iterator i = List.begin();
Files.push_back (*j);
}
- unsigned long EndOfSource = Files.size();
+ unsigned long const EndOfSource = Files.size();
if (_system->AddStatusFiles(Files) == false)
return false;
// Decide if we can write to the files..
- string CacheFile = _config->FindFile("Dir::Cache::pkgcache");
- string SrcCacheFile = _config->FindFile("Dir::Cache::srcpkgcache");
+ string const CacheFile = _config->FindFile("Dir::Cache::pkgcache");
+ string const SrcCacheFile = _config->FindFile("Dir::Cache::srcpkgcache");
// Decide if we can write to the cache
bool Writeable = false;
else
if (SrcCacheFile.empty() == false)
Writeable = access(flNotFile(SrcCacheFile).c_str(),W_OK) == 0;
-
+ if (Debug == true)
+ std::clog << "Do we have write-access to the cache files? " << (Writeable ? "YES" : "NO") << std::endl;
+
if (Writeable == false && AllowMem == false && CacheFile.empty() == false)
return _error->Error(_("Unable to write to %s"),flNotFile(CacheFile).c_str());
if (CheckValidity(CacheFile,Files.begin(),Files.end(),OutMap) == true)
{
Progress.OverallProgress(1,1,1,_("Reading package lists"));
+ if (Debug == true)
+ std::clog << "pkgcache.bin is valid - no need to build anything" << std::endl;
return true;
}
+ else if (Debug == true)
+ std::clog << "pkgcache.bin is NOT valid" << std::endl;
/* At this point we know we need to reconstruct the package cache,
begin. */
Map = new DynamicMMap(*CacheF,MMap::Public,MapSize);
if (_error->PendingError() == true)
return false;
+ if (Debug == true)
+ std::clog << "Open filebased MMap" << std::endl;
}
else
{
// Just build it in memory..
Map = new DynamicMMap(0,MapSize);
+ if (Debug == true)
+ std::clog << "Open memory Map (not filebased)" << std::endl;
}
// Lets try the source cache.
if (CheckValidity(SrcCacheFile,Files.begin(),
Files.begin()+EndOfSource) == true)
{
+ if (Debug == true)
+ std::clog << "srcpkgcache.bin is valid - populate MMap with it." << std::endl;
// Preload the map with the source cache
FileFd SCacheF(SrcCacheFile,FileFd::ReadOnly);
- unsigned long alloc = Map->RawAllocate(SCacheF.Size());
+ unsigned long const alloc = Map->RawAllocate(SCacheF.Size());
if ((alloc == 0 && _error->PendingError())
|| SCacheF.Read((unsigned char *)Map->Data() + alloc,
SCacheF.Size()) == false)
return false;
TotalSize = ComputeSize(Files.begin()+EndOfSource,Files.end());
-
+
// Build the status cache
pkgCacheGenerator Gen(Map.Get(),&Progress);
if (_error->PendingError() == true)
if (BuildCache(Gen,Progress,CurrentSize,TotalSize,
Files.begin()+EndOfSource,Files.end()) == false)
return false;
+
+ // FIXME: move me to a better place
+ Gen.FinishCache(Progress);
}
else
{
+ if (Debug == true)
+ std::clog << "srcpkgcache.bin is NOT valid - rebuild" << std::endl;
TotalSize = ComputeSize(Files.begin(),Files.end());
// Build the source cache
if (BuildCache(Gen,Progress,CurrentSize,TotalSize,
Files.begin()+EndOfSource,Files.end()) == false)
return false;
+
+ // FIXME: move me to a better place
+ Gen.FinishCache(Progress);
}
+ if (Debug == true)
+ std::clog << "Caches are ready for shipping" << std::endl;
if (_error->PendingError() == true)
return false;
if (BuildCache(Gen,Progress,CurrentSize,TotalSize,
Files.begin()+EndOfSource,Files.end()) == false)
return false;
-
+
+ // FIXME: move me to a better place
+ Gen.FinishCache(Progress);
+
if (_error->PendingError() == true)
return false;
*OutMap = Map.UnGuard();