[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

[Patch] (Long) apt-get patch to improve build from source commands.



The following patch adds two new options to apt-get.  It also slightly
alters the use of the 'source' option.

The point of this is to make it easier to build packages from source. 
"apt-get -b source packagename" works, but is fairly primitive. The two
new options make it much easier to pretend that source packages act like
binary ones.

'apt-get sourcetree packagename(s)'  will get and compile all the
build-depends of a package, and then compile the list of packages on the
command line.  It installs the sources in such a way so that can
continue compiling.  This is roughly the equivalent of "apt-get builddep
packagename && apt-get -b source packagename", but with the addition of
compiling everything from source and cleaning up afterwards.

'apt-get src-upgrade' tries to act as much like 'apt-get upgrade', but
will compile the new packages from source.  If the build-depends don't
exist, then the packages aren't made.

'apt-get -b source' will no longer compile the source packages... It
used to not test for builddeps.  Use sourcetree instead to get this
functionality.  (The '-b' flag won't give an error currently, though...)

I am currently working on making 'src-dist-upgrade' and 'build-world'
targets that will successively (re)compile more.  An option to rebuild
all packages that build depend on a changed package might also be
useful.

By adding a simple wrapper around the compilers (like ccache does) you
can alter the platform compiled for to be that of your machine.  (So it
is possible to have a more optimised distribution at the cost of
compiling at night whilst you are away from the machine...)

Since source packages used to be dropped in whatever directory you were
in when you called 'apt-get source', I have somewhat changed this to act
more like an extension of the regular download methds.  In this patch,
unless you specify the 'download only' flag, source packages are put
into the /var/cache/apt/archives/partial directory, where they are
compiled.  This can obviously be moved - but I haven't made it
customisable.  (Release early, release often.)

This code is probably of alpha quality, and I don't really know C++, so
try not to laugh too hard at it. ;-)  (Lots of the new code probably
should be moved out of apt-get.cc, and into the apt-pkg library.)  The
one obvious bug is that the new packages aren't recognised as being up
to date some of the time - so a repeated 'apt-get src-upgrade' will
recompile the same packages over and over again if you call it multiple
times.  Fixing this probably will require a policy decision though...

Steven



diff -u -r temp/apt-0.5.4/apt-pkg/acquire-item.cc
apt-0.5.4/apt-pkg/acquire-item.cc
--- temp/apt-0.5.4/apt-pkg/acquire-item.cc	Mon May  7 06:49:43 2001
+++ apt-0.5.4/apt-pkg/acquire-item.cc	Sat Apr 27 18:56:22 2002
@@ -597,7 +597,16 @@
 {
    Retries = _config->FindI("Acquire::Retries",0);
    
-   DestFile = flNotDir(URI);
+   // If you use the '-d' flag then you don't have to be root,
+   // and the files are downloaded into the current directory.
+   if (_config->FindB("APT::Get::Download-Only",false) == true)
+   {
+      DestFile = flNotDir(URI);
+   }
+   else
+   {
+      DestFile = _config->FindDir("Dir::Cache::Archives") + "partial/"
+ flNotDir(URI);
+   }
    
    // Create the item
    Desc.URI = URI;
Only in temp/apt-0.5.4/apt-pkg: configuration.d
diff -u -r temp/apt-0.5.4/apt-pkg/depcache.cc
apt-0.5.4/apt-pkg/depcache.cc
--- temp/apt-0.5.4/apt-pkg/depcache.cc	Sun May 27 06:36:04 2001
+++ apt-0.5.4/apt-pkg/depcache.cc	Mon May  6 14:29:59 2002
@@ -736,6 +736,50 @@
    }
 }
 									/*}}}*/
+// DepCache::MarkSource - We want to download the source	/*{{{*/
+//
---------------------------------------------------------------------
+/* */
+void pkgDepCache::MarkSource(PkgIterator const &Pkg, bool flag)
+{
+   // Simplifies other routines.
+   if (Pkg.end() == true)
+      return;
+
+   // Mark it.
+   StateCache &P = PkgState[Pkg->ID];
+   if (flag == true)
+   {
+      P.iFlags |= GetSrc;
+   }
+   else
+   {
+      P.iFlags &= ~(GetSrc);
+   }
+}
+									/*}}}*/
+// DepCache::MarkWait - We need to install this source later	/*{{{*/
+//
---------------------------------------------------------------------
+/* */
+void pkgDepCache::MarkWait(PkgIterator const &Pkg, bool flag)
+{
+   // Simplifies other routines.
+   if (Pkg.end() == true)
+      return;
+
+   // Mark it.
+   StateCache &P = PkgState[Pkg->ID];
+   if (flag == true)
+   {
+      MarkKeep(Pkg,false);
+      P.iFlags |= Wait;
+   }
+   else
+   {
+      MarkInstall(Pkg,false);
+      P.iFlags &= ~(Wait);
+   }
+}
+									/*}}}*/
 // DepCache::SetReInstall - Set the reinstallation flag			/*{{{*/
 //
---------------------------------------------------------------------
 /* */
@@ -749,6 +793,26 @@
       P.iFlags |= ReInstall;
    else
       P.iFlags &= ~ReInstall;
+   
+   AddStates(Pkg);
+   AddSizes(Pkg);
+}
+									/*}}}*/
+// DepCache::ForceInstall - Force installation of package			/*{{{*/
+//
---------------------------------------------------------------------
+/* */
+void pkgDepCache::ForceInstall(PkgIterator const &Pkg)
+{
+	// Try installing it
+   MarkInstall(Pkg,false);
+
+   RemoveSizes(Pkg);
+   RemoveStates(Pkg);
+   
+   StateCache &P = PkgState[Pkg->ID];
+   
+   // If failed - reinstall it.
+   if (P.Mode != ModeInstall) P.iFlags |= ReInstall;
    
    AddStates(Pkg);
    AddSizes(Pkg);
diff -u -r temp/apt-0.5.4/apt-pkg/depcache.h
apt-0.5.4/apt-pkg/depcache.h
--- temp/apt-0.5.4/apt-pkg/depcache.h	Tue Feb 20 07:03:17 2001
+++ apt-0.5.4/apt-pkg/depcache.h	Mon May  6 14:27:07 2002
@@ -59,7 +59,9 @@
                        DepCandPolicy = (1 << 4), DepCandMin = (1 <<
5)};
    
    // These flags are used in StateCache::iFlags
-   enum InternalFlags {AutoKept = (1 << 0), Purge = (1 << 1), ReInstall
= (1 << 2)};
+   enum InternalFlags {AutoKept = (1 << 0), Purge = (1 << 1),
+      	               ReInstall = (1 << 2), GetSrc = (1 << 3),
+      	               Wait = (1 << 4)};
       
    enum VersionTypes {NowVersion, InstallVersion, CandidateVersion};
    enum ModeList {ModeDelete = 0, ModeKeep = 1, ModeInstall = 2};
@@ -99,6 +101,8 @@
       inline bool NowBroken() const {return (DepState & DepNowMin) !=
DepNowMin;};
       inline bool InstBroken() const {return (DepState & DepInstMin) !=
DepInstMin;};
       inline bool Install() const {return Mode == ModeInstall;};
+      inline bool GetSource() const {return (iFlags & GetSrc) ==
GetSrc;};
+      inline bool GetWait() const {return (iFlags & Wait) == Wait;};
       inline VerIterator InstVerIter(pkgCache &Cache)
                 {return VerIterator(Cache,InstallVer);};
       inline VerIterator CandidateVerIter(pkgCache &Cache)
@@ -187,7 +191,10 @@
    void MarkDelete(PkgIterator const &Pkg,bool Purge = false);
    void MarkInstall(PkgIterator const &Pkg,bool AutoInst = true,
 		    unsigned long Depth = 0);
+   void MarkSource(PkgIterator const &Pkg,bool flag);
+   void MarkWait(PkgIterator const &Pkg,bool flag);
    void SetReInstall(PkgIterator const &Pkg,bool To);
+   void ForceInstall(PkgIterator const &Pkg);
    void SetCandidateVersion(VerIterator TargetVer);
    
    // This is for debuging
diff -u -r temp/apt-0.5.4/apt-pkg/packagemanager.cc
apt-0.5.4/apt-pkg/packagemanager.cc
--- temp/apt-0.5.4/apt-pkg/packagemanager.cc	Sun May 27 06:36:04 2001
+++ apt-0.5.4/apt-pkg/packagemanager.cc	Sat May  4 19:42:47 2002
@@ -289,7 +289,7 @@
    return true;
 }
 									/*}}}*/
-// PM::DepAdd - Add all dependents to the oder list			/*{{{*/
+// PM::DepAdd - Add all dependents to the order list			/*{{{*/
 //
---------------------------------------------------------------------
 /* This recursively adds all dependents to the order list */
 bool pkgPackageManager::DepAdd(pkgOrderList &OList,PkgIterator Pkg,int
Depth)
diff -u -r temp/apt-0.5.4/apt-pkg/packagemanager.h
apt-0.5.4/apt-pkg/packagemanager.h
--- temp/apt-0.5.4/apt-pkg/packagemanager.h	Mon May  7 05:24:08 2001
+++ apt-0.5.4/apt-pkg/packagemanager.h	Sun May  5 19:06:27 2002
@@ -74,6 +74,7 @@
    public:
       
    // Main action members
+   void SaveFilename(PkgIterator Pkg, string
Fname){FileNames[Pkg->ID]=Fname;};
    bool GetArchives(pkgAcquire *Owner,pkgSourceList *Sources,
 		    pkgRecords *Recs);
    OrderResult DoInstall();
Only in apt-0.5.4: bin
Only in apt-0.5.4: build
diff -u -r temp/apt-0.5.4/cmdline/apt-get.cc
apt-0.5.4/cmdline/apt-get.cc
--- temp/apt-0.5.4/cmdline/apt-get.cc	Sun Jul  1 23:59:04 2001
+++ apt-0.5.4/cmdline/apt-get.cc	Mon May  6 20:22:57 2002
@@ -50,12 +50,14 @@
 #include <sys/ioctl.h>
 #include <sys/stat.h>
 #include <sys/statvfs.h>
+#include <dirent.h>
 #include <signal.h>
 #include <unistd.h>
 #include <stdio.h>
 #include <errno.h>
 #include <regex.h>
 #include <sys/wait.h>
+
 									/*}}}*/
 
 using namespace std;
@@ -1605,7 +1607,7 @@
    return true;
 }
 									/*}}}*/
-// DoSource - Fetch a source archive					/*{{{*/
+// GetSource - Fetch a source archive					/*{{{*/
 //
---------------------------------------------------------------------
 /* Fetch souce packages */
 struct DscFile
@@ -1615,73 +1617,102 @@
    string Dsc;
 };
 
-bool DoSource(CommandLine &CmdL)
+bool GetSource(CacheFile &Cache, pkgRecords &Recs, pkgSrcRecords
&SrcRecs)
 {
-   CacheFile Cache;
-   if (Cache.Open(false) == false)
-      return false;
-
-   if (CmdL.FileSize() <= 1)
-      return _error->Error(_("Must specify at least one package to
fetch source for"));
-   
-   // Read the source list
-   pkgSourceList List;
-   if (List.ReadMainList() == false)
-      return _error->Error(_("The list of sources could not be
read."));
-   
-   // Create the text record parsers
-   pkgRecords Recs(Cache);
-   pkgSrcRecords SrcRecs(List);
-   if (_error->PendingError() == true)
-      return false;
-
    // Create the download object
    AcqTextStatus Stat(ScreenWidth,_config->FindI("quiet",0));   
    pkgAcquire Fetcher(&Stat);
-
-   DscFile *Dsc = new DscFile[CmdL.FileSize()];
    
-   // Load the requestd sources into the fetcher
    unsigned J = 0;
-   for (const char **I = CmdL.FileList + 1; *I != 0; I++, J++)
+   
+   for (unsigned I = 0; I < Cache->Head().PackageCount; I++)
    {
-      string Src;
-      pkgSrcRecords::Parser *Last =
FindSrc(*I,Recs,SrcRecs,Src,*Cache);
+      pkgCache::PkgIterator pkg(Cache,Cache.List[I]);
       
+      // Only installable packages
+      if (Cache[pkg].Install() == true ||
+      	 Cache[pkg].GetSource() == true)
+      	 J++;
+   }
+  
+   DscFile *Dsc = new DscFile[J];
+   string *SrcNames = new string[J];
+   
+   J = 0;
+   
+   string List;
+   for (unsigned I = 0; I < Cache->Head().PackageCount; I++)
+   {
+      pkgCache::PkgIterator pkg(Cache,Cache.List[I]);
+      
+      // Only installable packages
+      if (Cache[pkg].Install() == false &&
+      	 Cache[pkg].GetSource() == false)
+      	 continue;
+      
+      // Mark the fact we are using a source package if we haven't done
that yet. 
+      Cache->MarkSource(pkg,true);
+      
+      // Load the requested source into the fetcher
+      string Src;
+      pkgSrcRecords::Parser *Last =
FindSrc(pkg.Name(),Recs,SrcRecs,Src,Cache);
+   
       if (Last == 0)
-	 return _error->Error(_("Unable to find a source package for
%s"),Src.c_str());
+      {
+      	 ioprintf(c1out,_("Unable to find a source package for
%s\n"),Src.c_str());
+	 continue;
+      }
       
-      // Back track
-      vector<pkgSrcRecords::File> Lst;
-      if (Last->Files(Lst) == false)
-	 return false;
-
-      // Load them into the fetcher
-      for (vector<pkgSrcRecords::File>::const_iterator I = Lst.begin();
-	   I != Lst.end(); I++)
+      bool skip = false;
+      
+      // Hack - do not download multiple packages from the same source
+      for (int K = 0; K < J; K++)
       {
-	 // Try to guess what sort of file it is we are getting.
-	 if (I->Type == "dsc")
+	 if (Src == SrcNames[K])
 	 {
-	    Dsc[J].Package = Last->Package();
-	    Dsc[J].Version = Last->Version();
-	    Dsc[J].Dsc = flNotDir(I->Path);
+      	    skip = true;
+	    break;
 	 }
+      }
 	 
-	 // Diff only mode only fetches .diff files
-	 if (_config->FindB("APT::Get::Diff-Only",false) == true &&
-	     I->Type != "diff")
-	    continue;
-	 
-	 // Tar only mode only fetches .tar files
-	 if (_config->FindB("APT::Get::Tar-Only",false) == true &&
-	     I->Type != "tar")
-	    continue;
+      // Skip this package if we are already getting the source
+      if (skip == true) continue;
 	 
-	 new pkgAcqFile(&Fetcher,Last->Index().ArchiveURI(I->Path),
-			I->MD5Hash,I->Size,
-			Last->Index().SourceInfo(*Last,*I),Src);
+      // Save the name
+      SrcNames[J] = Src;
+   
+      // Back track
+      vector<pkgSrcRecords::File> Lst;
+      if (Last->Files(Lst) == false) continue;
+      
+      // Load them into the fetcher
+      for (vector<pkgSrcRecords::File>::const_iterator I = Lst.begin();
+     	 I != Lst.end(); I++)
+      {
+         // Try to guess what sort of file it is we are getting.
+      	 if (I->Type == "dsc")
+         {
+            Dsc[J].Package = Last->Package();
+     	    Dsc[J].Version = Last->Version();
+     	    Dsc[J].Dsc = flNotDir(I->Path);
+         }
+      
+      	 // Diff only mode only fetches .diff files
+      	 if (_config->FindB("APT::Get::Diff-Only",false) == true &&
+     	    I->Type != "diff")
+     	    continue;
+      
+      	 // Tar only mode only fetches .tar files
+      	 if (_config->FindB("APT::Get::Tar-Only",false) == true &&
+     	    I->Type != "tar")
+     	    continue;
+      
+      	 new pkgAcqFile(&Fetcher,Last->Index().ArchiveURI(I->Path),
+     		     I->MD5Hash,I->Size,
+		     Last->Index().SourceInfo(*Last,*I),Src);
       }
+      
+      J++;
    }
    
    // Display statistics
@@ -1757,13 +1788,14 @@
       for (unsigned I = 0; I != J; I++)
       {
 	 string Dir = Dsc[I].Package + '-' +
Cache->VS().UpstreamVersion(Dsc[I].Version.c_str());
+	 Dir = _config->FindDir("Dir::Cache::Archives") + "partial/" + Dir;
 	 
 	 // Diff only mode only fetches .diff files
 	 if (_config->FindB("APT::Get::Diff-Only",false) == true ||
 	     _config->FindB("APT::Get::Tar-Only",false) == true ||
 	     Dsc[I].Dsc.empty() == true)
 	    continue;
-
+	 
 	 // See if the package is already unpacked
 	 struct stat Stat;
 	 if (stat(Dir.c_str(),&Stat) == 0 &&
@@ -1776,32 +1808,16 @@
 	 {
 	    // Call dpkg-source
 	    char S[500];
-	    snprintf(S,sizeof(S),"%s -x %s",
+	    snprintf(S,sizeof(S),"cd %spartial/ && %s -x %s",
+	             _config->FindDir("Dir::Cache::Archives").c_str(),
 		     _config->Find("Dir::Bin::dpkg-source","dpkg-source").c_str(),
 		     Dsc[I].Dsc.c_str());
 	    if (system(S) != 0)
 	    {
 	       fprintf(stderr,_("Unpack command '%s' failed.\n"),S);
 	       _exit(1);
-	    }	    
-	 }
-	 
-	 // Try to compile it with dpkg-buildpackage
-	 if (_config->FindB("APT::Get::Compile",false) == true)
-	 {
-	    // Call dpkg-buildpackage
-	    char S[500];
-	    snprintf(S,sizeof(S),"cd %s && %s %s",
-		     Dir.c_str(),
-		    
_config->Find("Dir::Bin::dpkg-buildpackage","dpkg-buildpackage").c_str(),
-		     _config->Find("DPkg::Build-Options","-b -uc").c_str());
-	    
-	    if (system(S) != 0)
-	    {
-	       fprintf(stderr,_("Build command '%s' failed.\n"),S);
-	       _exit(1);
-	    }	    
-	 }      
+	    }
+	 }   
       }
       
       _exit(0);
@@ -1822,6 +1838,586 @@
    return true;
 }
 									/*}}}*/
+// CompileSource - Compile list of new source packages			/*{{{*/
+//
---------------------------------------------------------------------
+bool CompileSource(CacheFile &Cache, pkgRecords &Recs, pkgSrcRecords
&SrcRecs)
+{
+   unsigned ExpectedInst;
+   pkgProblemResolver Fix(Cache);
+   
+   bool compiled_something;
+   
+   // Don't do anything if told not to
+   if (_config->FindB("APT::Get::Download-only",false) == true)
+   {
+      return true;
+   }
+   
+   // Lock the archive directory
+   FileFd Lock;
+   if (_config->FindB("Debug::NoLocking",false) == false)
+   {
+      Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") +
"lock"));
+      if (_error->PendingError() == true)
+	 return _error->Error(_("Unable to lock the download directory"));
+   }
+   
+   /*
+    * Repeatly do the following:
+    * 1) Compile all packages compilable.
+    * 2) Install all marked packages we have compiled that are
installable.
+    *
+    * If either step doesn't do anything, then we are stuck, so stop.
+    */ 
+   while (true)
+   {
+      compiled_something = false;
+   
+      // Install pending packages.
+      SPtr<pkgPackageManager> PM= _system->CreatePM(Cache);
+      
+      // Try to compile the packages.
+      for (unsigned J = 0; J < Cache->Head().PackageCount; J++)
+      {
+      	 pkgCache::PkgIterator Pkg(Cache,Cache.List[J]);
+      
+         // Only look at things we haven't compiled yet
+         if (Cache[Pkg].GetSource() == false) continue;
+   
+         // This is a pure virtual package and there is a single
available provides
+         if (Cache[Pkg].CandidateVer == 0 && Pkg->ProvidesList != 0 &&
+      	 	 Pkg.ProvidesList()->NextProvides == 0)
+         {
+         	 pkgCache::PkgIterator Tmp = Pkg.ProvidesList().OwnerPkg();
+      	 	 Pkg = Tmp;
+         }
+   
+         string Src;
+      	 pkgSrcRecords::Parser *Last =
FindSrc(Pkg.Name(),Recs,SrcRecs,Src,Cache);
+         if (Last == 0) return false;
+
+         // We can now compile it
+
+      	 string Dir = Last->Package() + '-' +
Cache->VS().UpstreamVersion(Last->Version().c_str());
+      
+         // Call dpkg-buildpackage
+         char S[500];
+      	 snprintf(S,sizeof(S),"cd %spartial/%s && %s %s",
+            _config->FindDir("Dir::Cache::Archives").c_str(),
+            Dir.c_str(),
+      	   
_config->Find("Dir::Bin::dpkg-buildpackage","dpkg-buildpackage").c_str(),
+            _config->Find("DPkg::Build-Options","-b -uc -nc
-D").c_str());
+
+      	 // Notice the packages.
+         if (system(S) == 0)
+         {
+      	    // We have now compiled a file in this round
+      	    compiled_something = true;
+	    
+	    string StartDir = SafeGetCWD();
+	    
+	    string dir = _config->FindDir("Dir::Cache::Archives") +
_("partial");
+	    string archive = _config->FindDir("Dir::Cache::Archives");
+	    
+	    DIR *D = opendir(dir.c_str());
+	    chdir(Dir.c_str());
+	    
+	    for (struct dirent *Dirent = readdir(D); Dirent != 0; Dirent =
readdir(D))
+      	    {
+	       string fname = Dirent->d_name;
+	       
+	       string Filename = dir + "/" + fname;
+	       
+     	       // Hack - we assume the packages are .deb's for now.
+	       if (flExtension(fname) != _("deb")) continue;
+	       
+	       string::size_type len = 0;
+   
+      	       pkgCache::PkgIterator PkgFnd = Cache->PkgBegin();
+
+      	       for (pkgCache::PkgIterator I = Cache->PkgBegin(); I.end()
== false; I++)
+      	       {
+      	          string pkgname = ((string) I.Name()) + "_";
+   
+      	          // We want the package with the largest matching name
+      	          if (pkgname.length() < len) continue;
+      
+      	          // Is the package name a prefix to the file name?
+      	          if (fname.find(pkgname) == 0)
+      	          {
+                     len = pkgname.length();
+                     PkgFnd = I;
+      	          }
+      	       }
+	       
+	       ioprintf(cout, "Found Package: %s for %s\n", PkgFnd.Name(),
Filename.c_str());
+	     
+	       string destfile = _config->FindDir("Dir::Cache::Archives") +
fname;
+	       
+	       // Move file...
+	       rename(Filename.c_str(),destfile.c_str());
+	       
+	       // Remember filename
+	       PM->SaveFilename(PkgFnd, destfile);
+	       
+	       // Do we want to install this package?
+	       if (Cache[PkgFnd].GetSource() == false) continue;
+	       
+	       // Mark no longer need source
+   	       Cache->MarkSource(PkgFnd,false);
+	 
+   	       // Add to install list
+	       Cache->ForceInstall(PkgFnd);
+	 
+	       // Add to the 'wait' list
+	       Cache->MarkWait(PkgFnd,true);
+      	    };
+	    
+	    // Move back to original directory
+	    chdir(StartDir.c_str());
+	    closedir(D);
+	    
+	    // Remove the directory
+	    snprintf(S,sizeof(S),"cd %spartial && rm -rf %s ",
+               _config->FindDir("Dir::Cache::Archives").c_str(),
+               Dir.c_str());
+      	    system(S);
+         }
+      }
+   
+      // break out if we did nothing
+      if (!compiled_something) break;
+   
+      pkgProblemResolver Fix(Cache);
+   
+      // Add the waiting packages to the install list
+      for (unsigned J = 0; J < Cache->Head().PackageCount; J++)
+      {
+      	 pkgCache::PkgIterator Pkg(Cache,Cache.List[J]);
+      
+         // Is this package waiting to be installed?
+         if (Cache[Pkg].GetWait() == true)
+      	 {
+      	    Cache->ForceInstall(Pkg);
+         }
+         else if (Pkg->CurrentState == pkgCache::State::NotInstalled)
+      	 {
+      	    Fix.Protect(Pkg);
+            Cache->MarkKeep(Pkg);
+         }
+      }
+      
+      // Try to install as much as possible
+      Fix.ResolveByKeep();
+   
+      bool installing = false;
+   
+      // Do we have anything to install?
+      for (unsigned J = 0; J < Cache->Head().PackageCount; J++)
+      {
+         pkgCache::PkgIterator Pkg(Cache,Cache.List[J]);
+      
+      	 if (Cache[Pkg].Install() || Cache[Pkg].iFlags &
pkgDepCache::ReInstall)
+         {
+            // We are about to install it - forget the 'wait' flag
+	    Cache->MarkWait(Pkg,false);
+	 
+	    // We have something to install
+	    installing = true;
+	    
+	    ioprintf(cout,_("Want to install %s\n"),Pkg.Name());
+         }
+	 if (Cache[Pkg].Delete())
+	 {
+	    ioprintf(cout,_("Want to remove %s\n"),Pkg.Name());
+	 }
+      }
+   
+      // Done?
+      if (!installing) break;
+     
+      // Install and remove packages
+      while (true)
+      {
+         _system->UnLock();
+         pkgPackageManager::OrderResult Res = PM->DoInstall();
+      	 if (Res == pkgPackageManager::Failed || _error->PendingError()
== true)
+         	 return false;
+	  _system->Lock();
+         if (Res == pkgPackageManager::Completed)
+      	    break;
+      }
+   }
+   
+   // Scan for uncompiled packages and complain
+   for (unsigned J = 0; J < Cache->Head().PackageCount; J++)
+   {
+      pkgCache::PkgIterator Pkg(Cache,Cache.List[J]);
+   
+      if (Cache[Pkg].GetWait())
+      {
+     	 _error->Error(_("Want to install %s but cannot."),Pkg.Name());
+      }
+      if (Cache[Pkg].GetSource())
+      {
+     	 _error->Error(_("Want to compile %s but cannot."),Pkg.Name());
+      }
+   }
+   
+   // Pending errors?
+   if (_error->PendingError()) return false;
+
+   // Clean download directory
+   char S[500];
+   snprintf(S,sizeof(S),"cd %spartial && rm *",
+      _config->FindDir("Dir::Cache::Archives").c_str());
+   system(S);
+   
+   return true;
+}
+									/*}}}*/
+// DoSource - Fetch a source archive					/*{{{*/
+//
---------------------------------------------------------------------
+/* Fetch source packages */
+bool DoSource(CommandLine &CmdL)
+{
+   CacheFile Cache;
+   if (Cache.Open(false) == false)
+      return false;
+
+   if (CmdL.FileSize() <= 1)
+      return _error->Error(_("Must specify at least one package to
fetch source for"));
+   
+   // Read the source list
+   pkgSourceList List;
+   if (List.ReadMainList() == false)
+      return _error->Error(_("The list of sources could not be
read."));
+   
+   // Create the text record parsers
+   pkgRecords Recs(Cache);
+   pkgSrcRecords SrcRecs(List);
+   if (_error->PendingError() == true)
+      return false;
+   
+   unsigned ExpectedInst;
+   pkgProblemResolver Fix(Cache);
+   
+   // Find the wanted packages.
+   for (unsigned J = 0; J < Cache->Head().PackageCount; J++)
+   {
+      pkgCache::PkgIterator I(Cache,Cache.List[J]);
+      
+      if (Cache[I].Install() == true)
+	 Fix.Protect(I);
+
+      const char **J;
+      for (J = CmdL.FileList + 1; *J != 0; J++)
+      {
+     	 if (strcmp(*J,I.Name()) == 0)
+     	 {
+	    // Mark the package
+      	    Cache->MarkSource(I,true);
+	    Cache->MarkInstall(I,false);
+     	    break;
+     	 }
+      }
+   }
+   
+   Fix.InstallProtect();
+   
+   // Call the scored problem resolver
+   Fix.Resolve();
+   
+   // Sanity check
+   if (Cache->BrokenCount() != 0)
+   {
+      ShowBroken(c1out,Cache,false);
+      return _error->Error("Internal Error, InstallPackages was called
with broken packages!");
+   }
+   
+   // Actually get the source files
+   GetSource(Cache, Recs, SrcRecs);
+   
+   // Do not compile them...
+   
+   return true;
+}
+									/*}}}*/
+// TryToInstallDeps - Try to install a single package and all its
build-deps /*{{{*/
+//
---------------------------------------------------------------------
+bool TryToInstallDeps(pkgCache::PkgIterator Pkg,pkgDepCache &Cache,
+		  pkgProblemResolver &Fix,bool Remove, bool force,
+		  unsigned int &ExpectedInst,
+		  pkgRecords &Recs, pkgSrcRecords &SrcRecs)
+{
+   /* This is a pure virtual package and there is a single available 
+      provides */
+   if (Cache[Pkg].CandidateVer == 0 && Pkg->ProvidesList != 0 &&
+       Pkg.ProvidesList()->NextProvides == 0)
+   {
+      pkgCache::PkgIterator Tmp = Pkg.ProvidesList().OwnerPkg();
+      ioprintf(c1out,_("Note, selecting %s instead of %s\n"),
+	       Tmp.Name(),Pkg.Name());
+      Pkg = Tmp;
+   }
+   
+   // Handle the no-upgrade case
+   if (_config->FindB("APT::Get::upgrade",true) == false &&
+       Pkg->CurrentVer != 0 && force == false)
+   {
+      ioprintf(c1out,_("Skipping %s, it is already installed and
upgrade is not set.\n"),
+	       Pkg.Name());
+      return true;
+   }
+   
+   // Mark the package
+   Cache.MarkSource(Pkg, true);
+   
+   // Check if there is something at all to install
+   pkgDepCache::StateCache &State = Cache[Pkg];
+   if (Remove == true && Pkg->CurrentVer == 0)
+   {
+      /* We want to continue searching for regex hits, so we return
false here
+         otherwise this is not really an error. */
+      ioprintf(c1out,_("Package %s is not installed, so not
removed\n"),Pkg.Name());
+      return true;
+   }
+   
+   if (State.CandidateVer == 0 && Remove == false)
+   {
+      if (Pkg->ProvidesList != 0)
+      {
+	 ioprintf(c1out,_("Package %s is a virtual package provided by:\n"),
+		  Pkg.Name());
+	 
+	 pkgCache::PrvIterator I = Pkg.ProvidesList();
+	 for (; I.end() == false; I++)
+	 {
+	    pkgCache::PkgIterator Pkg = I.OwnerPkg();
+	    
+	    if (Cache[Pkg].CandidateVerIter(Cache) == I.OwnerVer())
+	    {
+	       if (Cache[Pkg].Install() == true && Cache[Pkg].NewInstall() ==
false)
+		  c1out << "  " << Pkg.Name() << " " << I.OwnerVer().VerStr() <<
+		  _(" [Installed]") << endl;
+	       else
+		  c1out << "  " << Pkg.Name() << " " << I.OwnerVer().VerStr() <<
endl;
+	    }      
+	 }
+	 c1out << _("You should explicitly select one to install.") << endl;
+      }
+      else
+      {
+	 ioprintf(c1out,
+	 _("Package %s has no available version, but exists in the
database.\n"
+	   "This typically means that the package was mentioned in a
dependency and\n"
+	   "never uploaded, has been obsoleted or is not available with the
contents\n"
+	   "of sources.list\n"),Pkg.Name());
+	 
+	 string List;
+	 SPtrArray<bool> Seen = new bool[Cache.Head().PackageCount];
+	 memset(Seen,0,Cache.Head().PackageCount*sizeof(*Seen));
+	 pkgCache::DepIterator Dep = Pkg.RevDependsList();
+	 for (; Dep.end() == false; Dep++)
+	 {
+	    if (Dep->Type != pkgCache::Dep::Replaces)
+	       continue;
+	    if (Seen[Dep.ParentPkg()->ID] == true)
+	       continue;
+	    Seen[Dep.ParentPkg()->ID] = true;
+	    List += string(Dep.ParentPkg().Name()) + " ";
+	 }	    
+	 ShowList(c1out,_("However the following packages replace it:"),List);
+      }
+      
+      _error->Error(_("Package %s has no installation
candidate"),Pkg.Name());
+      return false;
+   }
+
+   Fix.Clear(Pkg);
+   Fix.Protect(Pkg);   
+   if (Remove == true)
+   {
+      Fix.Remove(Pkg);
+      Cache.MarkDelete(Pkg,_config->FindB("APT::Get::Purge",false));
+      return true;
+   }
+   
+   // Install it
+   Cache.MarkInstall(Pkg,false);
+   if (State.Install() == false)
+   {
+      if (_config->FindB("APT::Get::ReInstall",false) == true)
+      {
+	 if (Pkg->CurrentVer == 0 || Pkg.CurrentVer().Downloadable() == false)
+	    ioprintf(c1out,_("Sorry, re-installation of %s is not possible, it
cannot be downloaded.\n"),
+		     Pkg.Name());
+	 else
+	    Cache.SetReInstall(Pkg,true);
+      }      
+   }   
+   else
+      ExpectedInst++;
+   
+   string Src;
+   pkgSrcRecords::Parser *Last =
FindSrc(Pkg.Name(),Recs,SrcRecs,Src,Cache);
+   if (Last == 0)
+      return _error->Error(_("Unable to find a source package for
%s"),Src.c_str());
+   	 
+   // Process the build-dependencies
+   vector<pkgSrcRecords::Parser::BuildDepRec> BuildDeps;
+   if (Last->BuildDepends(BuildDeps) == false)
+     return _error->Error(_("Unable to get build-dependency information
for %s"),Src.c_str());
+   
+   if (BuildDeps.size() == 0)
+   {
+      return _error->Error(_("%s has no build
depends.\n"),Src.c_str());
+   }
+   
+   // Install the build-dependancies
+   vector <pkgSrcRecords::Parser::BuildDepRec>::iterator D;
+   for (D = BuildDeps.begin(); D != BuildDeps.end(); D++)
+   {
+      pkgCache::PkgIterator Pkg2 = Cache.FindPkg((*D).Package);
+      if (Pkg2.end() == true)
+     	 return _error->Error(_("%s dependency on %s cannot be satisfied
because the package %s cannot be found"),
+     			     
Last->BuildDepType((*D).Type),Src.c_str(),(*D).Package.c_str());
+      pkgCache::VerIterator IV = Cache[Pkg2].InstVerIter(Cache);
+      
+      if ((*D).Type == pkgSrcRecords::Parser::BuildConflict || 
+     	  (*D).Type == pkgSrcRecords::Parser::BuildConflictIndep)
+      {
+     	 /* 
+     	  * conflict; need to remove if we have an installed version 
+     	  * that satisfies the version criterial 
+     	  */
+     	 if (IV.end() == false && 
+     	    
Cache.VS().CheckDep(IV.VerStr(),(*D).Op,(*D).Version.c_str()) == true)
+     	   
TryToInstallDeps(Pkg2,Cache,Fix,true,false,ExpectedInst,Recs,SrcRecs);
+      } 
+      else 
+      {
+     	 /* 
+     	  * If this is a virtual package, we need to check the list of
+     	  * packages that provide it and see if any of those are
+     	  * installed
+     	  */
+   	 pkgCache::PrvIterator Prv = Pkg.ProvidesList();
+   	 for (; Prv.end() != true; Prv++)
+     	    if (Cache[Prv.OwnerPkg()].InstVerIter(Cache).end() == false)
+     	       break;
+
+     	 if (Prv.end() == true)
+     	 {
+     	    /* 
+     	     * depends; need to install or upgrade if we don't have the
+     	     * package installed or if the version does not satisfy the
+     	     * build dep. This is complicated by the fact that if we
+     	     * depend on a version lower than what we already have 
+     	     * installed it is not clear what should be done; in practice
+     	     * this case should be rare though and right now nothing
+     	     * is done about it :-( 
+     	     */
+     	    if (IV.end() == true ||
+     	      
Cache.VS().CheckDep(IV.VerStr(),(*D).Op,(*D).Version.c_str()) == false)
+     		 
TryToInstallDeps(Pkg2,Cache,Fix,false,false,ExpectedInst,Recs,SrcRecs);
+     	 }
+      } 	    
+   }
+   
+   // Install it with autoinstalling enabled.
+   if (State.InstBroken() == true)
+      Cache.MarkInstall(Pkg,true);
+   return true;
+}
+									/*}}}*/
+// HasBuildDeps - Can package be compiled?	      	             	/*{{{*/
+//
---------------------------------------------------------------------
+bool HasBuildDeps(pkgCache::PkgIterator Pkg,pkgDepCache &Cache,
+		  pkgRecords &Recs, pkgSrcRecords &SrcRecs)
+{
+   /* This is a pure virtual package and there is a single available 
+      provides */
+   if (Cache[Pkg].CandidateVer == 0 && Pkg->ProvidesList != 0 &&
+       Pkg.ProvidesList()->NextProvides == 0)
+   {
+      pkgCache::PkgIterator Tmp = Pkg.ProvidesList().OwnerPkg();
+      ioprintf(c1out,_("Note, selecting %s instead of %s\n"),
+	       Tmp.Name(),Pkg.Name());
+      Pkg = Tmp;
+   }
+      
+   // Check if there is something at all to install
+   pkgDepCache::StateCache &State = Cache[Pkg];
+   
+   if (State.CandidateVer == 0)
+   {
+      return false;
+   }
+      
+   string Src;
+   pkgSrcRecords::Parser *Last =
FindSrc(Pkg.Name(),Recs,SrcRecs,Src,Cache);
+   
+   // No source package?
+   if (Last == 0) return false;
+   	 
+   // Process the build-dependencies
+   vector<pkgSrcRecords::Parser::BuildDepRec> BuildDeps;
+   
+   // No build deps - assume we can make it...
+   if (Last->BuildDepends(BuildDeps) == false) return true;
+   if (BuildDeps.size() == 0) return true;
+
+   // Test the build-dependancies
+   vector <pkgSrcRecords::Parser::BuildDepRec>::iterator D;
+   for (D = BuildDeps.begin(); D != BuildDeps.end(); D++)
+   {
+      pkgCache::PkgIterator Pkg2 = Cache.FindPkg((*D).Package);
+      
+      // Cannot find package?
+      if (Pkg2.end() == true) return false;
+      
+      pkgCache::VerIterator IV = Cache[Pkg2].InstVerIter(Cache);
+      
+      if ((*D).Type == pkgSrcRecords::Parser::BuildConflict || 
+     	  (*D).Type == pkgSrcRecords::Parser::BuildConflictIndep)
+      {
+     	 /* 
+     	  * conflict; need to remove if we have an installed version 
+     	  * that satisfies the version criterial
+     	  */
+     	 if (IV.end() == false && 
+     	    
Cache.VS().CheckDep(IV.VerStr(),(*D).Op,(*D).Version.c_str()) == true)
+     	    return false;
+      } 
+      else 
+      {
+     	 /* 
+     	  * If this is a virtual package, we need to check the list of
+     	  * packages that provide it and see if any of those are
+     	  * installed
+     	  */
+   	 pkgCache::PrvIterator Prv = Pkg.ProvidesList();
+   	 for (; Prv.end() != true; Prv++)
+     	    if (Cache[Prv.OwnerPkg()].InstVerIter(Cache).end() == false)
+     	       break;
+
+     	 if (Prv.end() == true)
+     	 {
+     	    /* 
+     	     * Depending on the wrong version is bad...
+     	     */
+     	    if (IV.end() == true ||
+     	      
Cache.VS().CheckDep(IV.VerStr(),(*D).Op,(*D).Version.c_str()) == false)
+     		  return false;
+     	 }
+      } 	    
+   }
+   
+   // Can install it
+   return true;
+}
+									/*}}}*/
 // DoBuildDep - Install/removes packages to satisfy build dependencies 
/*{{{*/
 //
---------------------------------------------------------------------
 /* This function will look at the build depends list of the given
source 
@@ -1937,7 +2533,242 @@
    return true;
 }
 									/*}}}*/
+// FixSource - Make sure we are loading all package dependancies as
source /*{{{*/
+//
---------------------------------------------------------------------
+bool FixSource(CacheFile &Cache,pkgRecords &Recs,pkgSrcRecords
&SrcRecs,
+               pkgProblemResolver &Fix)
+{
+   int  count = 0;
+   
+   while (count < 50)
+   {
+      unsigned int ExpectedInst = 0;
+   
+      for (unsigned I = 0; I < Cache->Head().PackageCount; I++)
+      {
+      	 pkgCache::PkgIterator pkg(Cache,Cache.List[I]);
+	 
+	 if (Cache[pkg].Install() == false)
+	 {
+	    // Not using this package.
+	    Cache->MarkSource(pkg,false);
+	    continue;
+	 }
+      
+      	 // Only installable packages
+	 if (Cache[pkg].GetSource() == true &&
!HasBuildDeps(pkg,Cache,Recs,SrcRecs))
+	 {
+	    // Try to install the source dependancies if not already there
+      	   
TryToInstallDeps(pkg,Cache,Fix,true,false,ExpectedInst,Recs,SrcRecs);
+      	    continue;
+	 }
+      }
+      
+      // Fix any problems
+      Fix.InstallProtect();
+      if (Fix.Resolve(true) == false)
+      {
+         _error->Discard();
+         
+         // We need to loop again.
+         ExpectedInst++;
+      }
+      
+      // No extra packages?
+      if (ExpectedInst == 0) return true;
+      
+      ioprintf(cout, "Looping in FixSource: %d",count); 
+   }
+   
+   // Timeout - print problems.
+   Fix.InstallProtect();
+   Fix.Resolve(true);
+   
+   return false;
+}
+									/*}}}*/
+// DoSourceTree - Install packages from the command line	     	/*{{{*/
+//
---------------------------------------------------------------------
+/* Install named packages */
+bool DoSourceTree(CommandLine &CmdL)
+{
+   CacheFile Cache;
+   if (Cache.Open(true) == false)
+      return false;
+
+   if (CmdL.FileSize() <= 1)
+      return _error->Error(_("Must specify at least one package to
check sources for"));
+   
+   // Read the source list
+   pkgSourceList List;
+   if (List.ReadMainList() == false)
+      return _error->Error(_("The list of sources could not be
read."));
+   
+   // Create the text record parsers
+   pkgRecords Recs(Cache);
+   pkgSrcRecords SrcRecs(List);
+   if (_error->PendingError() == true)
+      return false;
+
+   // Create the download object
+   AcqTextStatus Stat(ScreenWidth,_config->FindI("quiet",0));   
+   pkgAcquire Fetcher(&Stat);
+   
+   unsigned int ExpectedInst = 0;
+   unsigned int Packages = 0;
+   pkgProblemResolver Fix(Cache);
+
+   for (const char **I = CmdL.FileList + 1; *I != 0; I++)
+   {
+      string Src;
+      pkgSrcRecords::Parser *Last =
FindSrc(*I,Recs,SrcRecs,Src,*Cache);
+      if (Last == 0)
+	 return _error->Error(_("Unable to find a source package for
%s"),Src.c_str());
+	 
+            // Process the build-dependencies
+      vector<pkgSrcRecords::Parser::BuildDepRec> BuildDeps;
+      if (Last->BuildDepends(BuildDeps) == false)
+      	return _error->Error(_("Unable to get build-dependency
information for %s"),Src.c_str());
+   
+      if (BuildDeps.size() == 0)
+      {
+	 ioprintf(c1out,_("%s has no build depends.\n"),Src.c_str());
+	 continue;
+      }
+      
+      // Install the requested packages
+      unsigned int ExpectedInst = 0;
+      
+      pkgProblemResolver Fix(Cache);
+      
+      // Locate the package
+      pkgCache::PkgIterator Pkg = Cache->FindPkg(Src);
+      
+      // Get the source for its dependancies recursively
+     
TryToInstallDeps(Pkg,Cache,Fix,false,true,ExpectedInst,Recs,SrcRecs);
+           
+      // Now we check the state of the packages,
+      if (Cache->BrokenCount() != 0)
+	 return _error->Error(_("Some broken packages were found while trying
to process build-dependencies.\n"
+				"You might want to run `apt-get -f install' to correct these."));
+   }
+   
+   // Make the list of packages self-consistant
+   if (FixSource(Cache, Recs, SrcRecs, Fix) == false) return false;
+   
+   /* Print out a list of packages that are going to be installed extra
+      to what the user asked */
+   string XtraPkgList;
+   for (unsigned J = 0; J < Cache->Head().PackageCount; J++)
+   {
+      pkgCache::PkgIterator I(Cache,Cache.List[J]);
+      if ((*Cache)[I].Install() == false)
+     	 continue;
+
+      const char **J;
+      for (J = CmdL.FileList + 1; *J != 0; J++)
+     	 if (strcmp(*J,I.Name()) == 0)
+     	     break;
+      
+      if (*J == 0)
+     	 XtraPkgList += string(I.Name()) + " ";
+   }
+   
+   ShowList(c1out,_("The following extra packages will be
installed:"),XtraPkgList);
+   
+   // Download the source files
+   GetSource(Cache, Recs, SrcRecs);
+   
+   // Compile them
+   return (CompileSource(Cache, Recs, SrcRecs));
+}
+									/*}}}*/
+// DoSrcUpgrade - Upgrade all packages from source		     	/*{{{*/
+//
---------------------------------------------------------------------
+/* Upgrade all packages without installing new packages or erasing old
+   packages, using src packages. */
+bool DoSrcUpgrade(CommandLine &CmdL)
+{
+   CacheFile Cache;
+   if (Cache.OpenForInstall() == false || Cache.CheckDeps() == false)
+      return false;
+   
+   // Read the source list
+   pkgSourceList List;
+   if (List.ReadMainList() == false)
+      return _error->Error(_("The list of sources could not be
read."));
+   
+   // Create the text record parsers
+   pkgRecords Recs(Cache);
+   pkgSrcRecords SrcRecs(List);
+   if (_error->PendingError() == true)
+      return false;
+   
+   pkgProblemResolver Fix(Cache);
 
+   if (Cache->BrokenCount() != 0)
+      return false;
+   
+   // Upgrade all installed packages
+   for (pkgCache::PkgIterator I = Cache->PkgBegin(); I.end() == false;
I++)
+   {
+      if (Cache[I].Install() == true)
+	 Fix.Protect(I);
+	  
+      if (_config->FindB("APT::Ignore-Hold",false) == false)
+	 if (I->SelectedState == pkgCache::State::Hold)
+	    continue;
+      
+      if (I->CurrentVer != 0 && HasBuildDeps(I,Cache,Recs,SrcRecs))
+      {
+         if (Cache[I].InstallVer != 0)
+         {
+      	    Cache->MarkInstall(I,false);
+         }
+      }
+   }
+      
+   // Hold back held packages.
+   if (_config->FindB("APT::Ignore-Hold",false) == false)
+   {
+      for (pkgCache::PkgIterator I = Cache->PkgBegin(); I.end() ==
false; I++)
+      {
+	 if (I->SelectedState == pkgCache::State::Hold)
+	 {
+	    Fix.Protect(I);
+	    Cache->MarkKeep(I);
+	    Cache->MarkSource(I, false);
+	 }
+      }
+   }
+   
+   // Minimise download
+   Fix.ResolveByKeep();
+   
+   // Make sure build-dependencies exist
+   if (FixSource(Cache,Recs,SrcRecs,Fix) == false)
+   {
+      ShowBroken(c1out,Cache,false);
+      return _error->Error(_("Internal Error, SrcUpgrade broke
stuff"));
+   }
+   
+   // Show all the various warning indicators
+   ShowDel(c1out,Cache);
+   ShowNew(c1out,Cache);
+   ShowHold(c1out,Cache);
+   if (_config->FindB("APT::Get::Show-Upgraded",false) == true)
+      ShowUpgraded(c1out,Cache);
+   ShowDowngraded(c1out,Cache);
+   ShowEssential(c1out,Cache);
+   Stats(c1out,Cache);
+   
+   // Download the source files
+   GetSource(Cache, Recs, SrcRecs);
+   
+   // Compile them
+   return(CompileSource(Cache, Recs, SrcRecs));
+}
+									/*}}}*/
 // DoMoo - Never Ask, Never Tell					/*{{{*/
 //
---------------------------------------------------------------------
 /* */
@@ -2126,6 +2957,8 @@
                                    {"autoclean",&DoAutoClean},
                                    {"check",&DoCheck},
       				   {"source",&DoSource},
+				   {"sourcetree",&DoSourceTree},
+				   {"src-upgrade",&DoSrcUpgrade},
 				   {"moo",&DoMoo},
       				   {"help",&ShowHelp},
                                    {0,0}};


-- 
To UNSUBSCRIBE, email to deity-request@lists.debian.org
with a subject of "unsubscribe". Trouble? Contact listmaster@lists.debian.org



Reply to: