| #!/usr/bin/env python |
| import os |
| from os.path import walk, splitext, exists, basename, dirname |
| import sys |
| |
| import output |
| from output import * |
| import portage |
| import xpak |
| import string |
| from string import strip, find, replace, split |
| import commands |
| import shutil |
| import re |
| import time |
| |
| #number of ebuilds merged |
| merged=0 |
| # there can only be one action specified; the default action is to merge |
| # specified packages, not update. |
| actions=["help", |
| "rebuild", |
| "search", |
| "update", |
| "clean", |
| "zap", |
| "rsync"] |
| # emerge (prints help) |
| # emerge help (prints help) |
| # emerge help config (prints config file management help) |
| |
| # emerge rsync |
| |
| # the default behavior of emerge is to satisfy dependencies specified by merging packages. |
| # emerge system (satisfy deps for "system" package class) |
| # emerge world (equivalent to emerge system since "world" doesn't offer any new deps) |
| # emerge <depstring> (satisfy dep by merging most recent version of app available that satisfies dep) |
| # NOTE: this will cause something to be merged again if the dep happens to be already satisfied |
| # "unslotted" is invalid for a package class here because it only applies to already-installed ebuilds. |
| # pkgclass support: "system", "world", <depstring>, <pkgname>, <ebuild>, <tbz2> |
| |
| # no changes here except we use "update" rather than "--update" |
| # emerge update <pkgclass/depstring/pkgname> |
| # emerge update (defaults to "world") |
| # "emerge update <ebuild/tbz2> doesn't make too much sense. |
| # But it should do the same thing are "emerge <ebuild/tbz2>". |
| |
| # rebuild is like "update" except that all packages will be rebuilt even if they currently are installed. |
| # emerge rebuild (defaults to "world") |
| # emerge rebuild <pkgclass/depstring/pkgname/ebuild> |
| # "emerge rebuild <ebuild/tbz2> doesn't make too much sense. |
| # But it should do the same thing are "emerge <ebuild/tbz2>". |
| |
| # emerge search (invalid) |
| # emerge search <regex> |
| |
| # clean does "cleaning" only; it does not do anything that would generally be considered dangerous |
| # emerge clean (defaults to "world"; apply default AUTOCLEAN behavior defined in make.conf) |
| # emerge --rev clean (defaults to "world", cleans out old revisions) |
| # emerge --slot clean (defaults to "world", cleans out old stuff from slots) |
| # emerge [--rev/--slot] clean <pkgclass> |
| # AUTOCLEAN var can be set to "slot", "rev", or "none/off/etc". |
| |
| # zap flat out removes stuff. No holds barred. Can be dangerous. |
| # emerge zap <pkgclass> (only "unslotted" is valid, which is still dangerous. Or a dep "=foo/bar") |
| # NOTE: of course, "emerge zap" alone is invalid and will *not* default to a "world" package class |
| |
| pkgclass=["world","system","unslotted"] |
| helpclass=["config"] |
| |
| # These next options are the only long options for Portage 1.8.9 except for "--help" (equiv. to "help") |
| |
| options=["--autoclean", |
| "--buildpkg", |
| "--debug", |
| "--fetchonly", |
| "--noreplace", |
| "--onlydeps", |
| "--pretend", |
| "--usepkg", |
| "--verbose", |
| "--emptytree", |
| "--rev", |
| "--slot", |
| "--help"] |
| |
| # Here are the currently supported short mappings. |
| |
| shortmapping={"a":"--autoclean", |
| "b":"--buildpkg", |
| "d":"--debug", |
| "f":"--fetchonly", |
| "h":"--help", |
| "k":"--usepkg", |
| "n":"--noreplace", |
| "o":"--onlydeps", |
| "p":"--pretend", |
| "v":"--verbose" } |
| |
| # end of spec |
| |
| myaction=None |
| myopts=[] |
| mymode=[] |
| myfiles=[] |
| edebug=0 |
| |
| # process short actions and options |
| for x in sys.argv[1:]: |
| if x[0:1]=="-"and not x[1:2]=="-": |
| for y in x[1:]: |
| if shortmapping.has_key(y): |
| sys.argv.append(shortmapping[y]) |
| else: |
| print y |
| print "!!! Error: -"+y+" is an invalid short action or option." |
| sys.argv.remove(x) |
| |
| # process the command arguments |
| for x in sys.argv[1:]: |
| if x=="update" or x=="--unmerge" or x=="--world": |
| print |
| if x=="update": |
| print " "+red("* WARNING:"),green("emerge update"),"has been deprecated, use",green("emerge --update"),"instead." |
| elif x=="--unmerge": |
| print " "+red("* WARNING:"),green("emerge --unmerge"),"has been deprecated, use",green("emerge --zap"),"instead." |
| elif x=="--world": |
| print " "+red("* WARNING:"),green("emerge --world"),"has been deprecated, use",green("emerge world"),"instead." |
| print " "+red("*"),"Read",green("emerge --help"),"to get a complete overview of the interface changes." |
| print |
| sys.exit(1) |
| |
| if len(x)>=2: |
| if x[0:1]=="-": |
| if x[1:2]=="-": |
| if x in actions: |
| if myaction: |
| print "!!! Error: more than one action specified on command-line." |
| print "!!! Available actions:",actions |
| sys.exit(1) |
| else: |
| myaction=x |
| elif x in options: |
| myopts.append(x) |
| else: |
| print "!!! Error:",x,"is an invalid action or option." |
| sys.exit(1) |
| else: |
| for y in x[1:]: |
| if shortmapping.has_key(y): |
| myopts.append(shortmapping[y]) |
| else: |
| print y |
| print "!!! Error: -"+y+" is an invalid short action or option." |
| elif x in modes: |
| if len(mymode)>=1: |
| print "!!! Error: more than one mode specified on command-line." |
| print "!!! Available modes:",modes |
| sys.exit(1) |
| else: |
| mymode.append(x) |
| else: |
| # this little conditional helps tab completion |
| if x[-1]=="/": |
| myfiles.append(x[:-1]) |
| else: |
| myfiles.append(x) |
| |
| # check if root user is the current user for the actions where emerge needs this |
| if os.getuid()!=0 and "--help"!=myaction and "--search"!=myaction: |
| print "!!! to be able to perform the requested action, emerge must be run by root." |
| sys.exit(1) |
| |
| # search functionality |
| class search: |
| |
| # |
| # class constants |
| # |
| VERSION_SHORT=1 |
| VERSION_RELEASE=2 |
| |
| # |
| # public interface |
| # |
| def __init__(self,searchkey=""): |
| """Searches the available and installed packages for the supplied search key. |
| The list of available and installed packages is created at object instantiation. |
| This makes successive searches faster.""" |
| self.searchkey = searchkey |
| self.treecache = portage.portagetree() |
| self.treecache.populate() |
| self.installcache = portage.vartree() |
| self.installcache.populate() |
| self.re_portagedir = re.compile('/usr/portage/') |
| self.re_description = re.compile('DESCRIPTION="') |
| self.initResults() |
| |
| def setKey(self,searchkey): |
| """changes the search key""" |
| self.searchkey = searchkey |
| self.initResults() |
| |
| def execute(self): |
| """Performs the saerch for the supplied search key""" |
| if self.searchkey: |
| self.initResults() |
| for package in self.treecache.getallnodes(): |
| package_parts=package.split("/") |
| if package_parts: |
| if self.searchkey != "*": |
| if re.search(self.searchkey.lower(), package_parts[1].lower()): |
| self.packagematches.append(package) |
| else: |
| self.packagematches.append(package) |
| |
| def output(self): |
| """Outputs the results of the search.""" |
| print "[ Results for search key : "+white(self.searchkey)+" ]" |
| print "[ Applications found : "+white(str(len(self.packagematches)))+" ]" |
| print " " |
| for match in self.packagematches: |
| full_package = strip(self.treecache.dep_bestmatch(match)) |
| if len(full_package) > 1: |
| print green("*")+" "+white(match) |
| print " ", "Latest version Available:",self.getVersion(full_package, search.VERSION_RELEASE) |
| print " ", self.getInstallationStatus(match) |
| print " ", "Description:",self.getDescription(self.getFullPath(match, full_package),self.getVersion(full_package, search.VERSION_SHORT)) |
| print " " |
| else: |
| print green("*")+" "+white(match)+" "+red("[ Masked ]") |
| print " " |
| |
| # |
| # private interface |
| # |
| def initResults(self): |
| self.packagematches = [] |
| |
| def getInstallationStatus(self,package): |
| installed_package = self.installcache.dep_bestmatch(package) |
| result = "" |
| version = self.getVersion(installed_package,search.VERSION_RELEASE) |
| if len(version) > 1: |
| result = "Latest version Installed: "+version |
| else: |
| result = "Latest version Installed: [ Not Installed ]" |
| return result |
| |
| def getDescription(self,ebuildPath,packageVersion): ## Gets description from latest ebuild ## |
| file = open(ebuildPath) |
| result = ""; |
| indescription=0 |
| while 1: |
| data = file.readline() |
| if data: |
| if self.re_description.match(data.upper()): |
| indescription=1 |
| p = self.re_description.split(data) |
| p = p[1].split('"') |
| result = p[0].strip() |
| if len(p)>1: |
| break |
| elif indescription==1: |
| if '"' in data: |
| p = data.split('"') |
| result = result+" "+p[0].strip() |
| indescription = 0 |
| else: |
| result = result+" "+data.strip() |
| else: |
| break; |
| result = replace(result, "${PV}", packageVersion) |
| result = replace(result, "${pv}", packageVersion) |
| result = replace(result, "${description}", "KDE " + packageVersion + " ") |
| result = replace(result, "\n", "") |
| file.close() |
| # format and wrap the description text nicely |
| wraplength=60 |
| formatted_result="" |
| while len(result)>wraplength: |
| lineposition=0 |
| position=wraplength |
| while result[lineposition+position]!=' ' and position != 0: |
| position = position-1 |
| if position==0: |
| position=wraplength |
| if len(formatted_result)==0: |
| formatted_result=result[:position] |
| else: |
| formatted_result=formatted_result+"\n "+result[:position] |
| result=result[position+1:] |
| if wraplength==60: |
| wraplength=73 |
| formatted_result=formatted_result+"\n "+result |
| return formatted_result |
| |
| def getFullPath(self,packageShortName,packageFullName): ## Returns the full path of the ebuild ## |
| absolute_path = '/usr/portage/' + strip(packageShortName) |
| package_parts = packageFullName.split("/") |
| result = absolute_path + "/" + package_parts[1] + ".ebuild" |
| return result |
| |
| def getVersion(self,full_package,detail): |
| if len(full_package) > 1: |
| package_parts = portage.catpkgsplit(full_package) |
| if detail == search.VERSION_RELEASE and package_parts[3] != 'r0': |
| result = package_parts[2]+ "-" + package_parts[3] |
| else: |
| result = package_parts[2] |
| else: |
| result = "" |
| return result |
| |
| #build our package digraph |
| def getsyslist(): |
| if os.path.exists(portage.profiledir+"/packages"): |
| pfile=portage.profiledir+"/packages" |
| else: |
| print "!!! Couldn't find",portage.profiledir+"/packages;" |
| print "\"system\" mode unavailable." |
| sys.exit(1) |
| myfile=open(pfile,"r") |
| mylines=myfile.readlines() |
| myfile.close() |
| |
| mynewlines=[] |
| for x in mylines: |
| myline=string.join(string.split(x)) |
| if not len(myline): |
| continue |
| elif myline[0]=="#": |
| continue |
| elif myline[0]!="*": |
| continue |
| myline=myline[1:] |
| mynewlines.append(myline.strip()) |
| |
| return mynewlines |
| |
| class depgraph: |
| |
| def __init__(self,mymode,myaction,myopts): |
| self.mymode=mymode |
| self.myaction=myaction |
| self.myopts=myopts |
| virts=portage.getvirtuals("/") |
| self.db={} |
| self.db["/"]={"virtuals":virts,"vartree":portage.vartree("/",virts),"porttree":portage.portagetree("/",virts),"bintree":portage.binarytree("/",virts)} |
| if portage.root!="/": |
| pr=portage.root |
| virts=portage.getvirtuals(pr) |
| self.db[pr]={"virtuals":virts,"vartree":portage.vartree(pr,virts),"porttree":portage.portagetree(pr,virts),"bintree":portage.binarytree(pr,virts)} |
| self.digraph=portage.digraph() |
| self.orderedkeys=[] |
| #the following is so we have an empty vartree (used in emerge update calculations) |
| self.emptytree=portage.vartree("/",virts,self.db["/"]["vartree"]) |
| self.emptytree.tree={} |
| if "--emptytree" in myopts: |
| print "Running in empty tree mode" |
| self.db["/"]["vartree"].tree=self.emptytree.tree |
| self.db["/"]["vartree"].populated=1 |
| self.db["/"]["vartree"].inject("virtual/glibc-1.0") |
| self.outdatedpackages=[] |
| def create(self,mybigkey,myparent=None,addme=1): |
| """creates the actual digraph of packages to merge. return 1 on success, 0 on failure |
| mybigkey = specification of package to merge; myparent = parent package (one depending on me); |
| addme = should I be added to the tree? (for the --onlydeps mode)""" |
| if mybigkey==None: |
| return |
| sys.stdout.write(".") |
| sys.stdout.flush() |
| if self.digraph.hasnode(mybigkey): |
| #if we've already hit this node before, we're already recursing on the dependencies. |
| #no need to recurse again. |
| if addme: |
| if myparent: |
| self.digraph.addnode(mybigkey,myparent) |
| return 1 |
| mytype,myroot,mykey=string.split(mybigkey) |
| if "--noreplace" in self.myopts: |
| if self.db[myroot]["vartree"].exists_specific(mykey): |
| print "\n>>>",mykey,"already merged in",myroot+", skipping...\n" |
| return 1 |
| if mytype=="binary": |
| if not self.db[portage.root]["bintree"].exists_specific(mykey): |
| print "\n\n!!!",mytype,mykey,"not found (possibly blocked by package.mask)\n" |
| sys.exit(1) |
| mypkgparts=portage.catpkgsplit(mykey) |
| mytbz2=xpak.tbz2(self.db[portage.root]["bintree"].getname(mykey)) |
| edepend=["",string.join(mytbz2.getelements("RDEPEND")," "),mytbz2.getfile("SLOT",mypkgparts[2])] |
| elif mytype=="ebuild": |
| if not self.db[myroot]["porttree"].exists_specific(mykey): |
| print "\n\n!!!",mytype,mykey,"not found (possibly blocked by package.mask)\n" |
| sys.exit(1) |
| mydep={} |
| myebuild=self.db[myroot]["porttree"].getname(mykey) |
| edepend=portage.doebuild(myebuild,"depend",myroot,1,edebug) |
| if edepend==1: |
| print "!!! emerge aborting." |
| sys.exit(1) |
| elif mytype=="blocks": |
| edepend=None |
| parenttype,parentroot,parentkey=string.split(myparent) |
| mykeyparts=portage.catpkgsplit(mykey) |
| parentkeyparts=portage.catpkgsplit(parentkey) |
| # only add blocking deps that aren't from the same package that is installing |
| if mykeyparts[0]!=parentkeyparts[0] or mykeyparts[1]!=parentkeyparts[1]: |
| self.digraph.addnode(mybigkey,myparent) |
| |
| if edepend: |
| mydep={} |
| if myroot=="/": |
| mydep["/"]=edepend[0]+" "+edepend[1] |
| else: |
| mydep["/"],mydep[myroot]=edepend |
| if addme: |
| self.digraph.addnode(mybigkey,myparent) |
| for dep in mydep.keys(): |
| if "--update"==self.myaction: |
| mycheck=self.emptytree.depcheck(mydep[dep]) |
| else: |
| mycheck=self.db[dep]["vartree"].depcheck(mydep[dep]) |
| if mycheck[0]==0: |
| print "!!! depgraph.create() error: string format:",mydep |
| return 0 |
| for x in mycheck[1]: |
| mynew=self.match(x,dep,mykey) |
| if not self.digraph.hasnode(mynew): |
| if addme: |
| if not self.create(mynew,mybigkey): |
| return 0 |
| else: |
| if not self.create(mynew,None): |
| return 0 |
| else: |
| self.digraph.addnode(mynew,mybigkey) |
| |
| return 1 |
| |
| def altlist(self): |
| mygraph=self.digraph.copy() |
| dolist=["/"] |
| retlist=[] |
| for x in self.db.keys(): |
| self.db[x]["merge"]=[] |
| if x not in dolist: |
| dolist.append(x) |
| while (not mygraph.empty()): |
| mycurkey=mygraph.firstzero() |
| if not mycurkey: |
| print "!!! Error: circular dependencies" |
| sys.exit(1) |
| splitski=string.split(mycurkey) |
| if "--update"==self.myaction: |
| if not self.db["/"]["vartree"].exists_specific(splitski[2]): |
| self.db["/"]["merge"].append(splitski) |
| else: |
| self.db[splitski[1]]["merge"].append(splitski) |
| mygraph.delnode(mycurkey) |
| for x in dolist: |
| for y in self.db[x]["merge"]: |
| retlist.append(y) |
| return retlist |
| |
| def syscreate(self,mylines=[]): |
| for myline in mylines: |
| if "--update"==self.myaction or "--rebuild"==self.myaction: |
| self.create(self.match(myline,mykey="update (likely old /var/db/pkg entry)")) |
| else: |
| mycheck=self.db[portage.root]["vartree"].depcheck(myline) |
| if mycheck[0]==0: |
| print "\n!!! Warning:",myline,"has an invalid depstring\n" |
| continue |
| if mycheck[1]==None: |
| continue |
| for x in mycheck[1]: |
| self.create(self.match(myline,mykey="syscreate1")) |
| |
| def match(self,mydep,myroot=portage.root,mykey=None): |
| # support mutual exclusive deps |
| mydep2=mydep |
| if mydep2[0]=="!": |
| mydep2=mydep[1:] |
| |
| if self.myaction=="--rebuild": |
| myeb=self.db[portage.root]["vartree"].dep_bestmatch(mydep2) |
| if not self.db[portage.root]["porttree"].exists_specific(myeb): |
| self.outdatedpackages.append(myeb) |
| return None |
| else: |
| myeb=self.db[portage.root]["porttree"].dep_bestmatch(mydep2) |
| if not myeb: |
| if not mykey: |
| print "\n!!! Warning: couldn't find match for",mydep |
| else: |
| print "\n!!! Warning: couldn't find match for",mydep,"in",mykey |
| return None |
| |
| # handle filtering of virtual deps for the rebuild action |
| if self.myaction=="--rebuild" and myeb[0:len("virtual")]=="virtual": |
| return None |
| |
| # handle filtering of non slot packages |
| if "noslot" in mymode and self.db[portage.root]["vartree"].slotted(myeb)==1: |
| return None |
| |
| if mydep[0]=="!": |
| myk="blocks "+myroot+" "+myeb |
| else: |
| if "--usepkg" in self.myopts: |
| mypk=self.db[portage.root]["bintree"].dep_bestmatch(mydep) |
| if myeb==mypk: |
| myk="binary "+portage.root+" "+mypk |
| else: |
| myk="ebuild "+myroot+" "+myeb |
| else: |
| myk="ebuild "+myroot+" "+myeb |
| |
| return myk |
| |
| def display(self,mylist): |
| for x in mylist: |
| if x[0]=="blocks": |
| addl=""+red("B")+" " |
| print "["+x[0]+" "+addl+"]",red(x[2]) |
| else: |
| if self.db[x[1]]["vartree"].exists_specific(x[2]): |
| addl=" "+yellow("R")+" " |
| elif self.db[x[1]]["vartree"].exists_specific_cat(x[2]): |
| addl=" "+turquoise("U")+" " |
| else: |
| addl=" "+green("N")+" " |
| print "["+x[0]+" "+addl+"]",x[2],"to",x[1] |
| |
| def outdated(self): |
| return self.outdatedpackages |
| |
| def merge(self,mylist): |
| returnme=0 |
| #check for blocking dependencies |
| for x in mylist: |
| if x[0]=="blocks": |
| print "\n!!! Error: the "+x[2]+" package conflicts with this package and both can't be installed on the same system together." |
| sys.exit(1) |
| |
| #above line used by --fetchonly |
| for x in mylist: |
| myroot=x[1] |
| print ">>> emerge",x[2],"to",x[1] |
| #the last argument in the portage.doebuild() tells doebuild to *not* do dep checking |
| #(emerge is already handling that) |
| y=self.db[myroot]["porttree"].getname(x[2]) |
| if x[0]=="ebuild": |
| if "--fetchonly" in self.myopts: |
| retval=portage.doebuild(y,"fetch",myroot,0,edebug) |
| if retval: |
| print |
| print "!!! Fetch for",y,"failed, continuing..." |
| print |
| returnme=1 |
| elif "--buildpkg" in self.myopts: |
| #create pkg, then merge pkg |
| retval=portage.doebuild(y,"clean",myroot,0,edebug) |
| if retval: |
| print "!!! emerge aborting on ",y,"." |
| sys.exit(1) |
| retval=portage.doebuild(y,"package",myroot,0,edebug) |
| if retval: |
| print "!!! emerge aborting on ",y,"." |
| sys.exit(1) |
| #dynamically update our database |
| self.db[portage.root]["bintree"].inject(x[2]) |
| mytbz2=self.db[portage.root]["bintree"].getname(x[2]) |
| retval=portage.pkgmerge(mytbz2,myroot) |
| if retval==None: |
| print "!!! emerge aborting on ",y,"." |
| sys.exit(1) |
| else: |
| retval=portage.doebuild(y,"clean",myroot,0,edebug) |
| if retval: |
| print "!!! emerge aborting on ",y,"." |
| sys.exit(1) |
| retval=portage.doebuild(y,"merge",myroot,0,edebug) |
| if retval: |
| print "!!! emerge aborting on ",y,"." |
| sys.exit(1) |
| #dynamically update our database |
| elif x[0]=="binary": |
| #merge the tbz2 |
| mytbz2=self.db[portage.root]["bintree"].getname(x[2]) |
| retval=portage.pkgmerge(mytbz2,x[1]) |
| if retval==None: |
| print "!!! emerge aborting on ",y,"." |
| sys.exit(1) |
| #need to check for errors |
| self.db[x[1]]["vartree"].inject(x[2]) |
| if "--autoclean" in self.myopts: |
| retval=portage.doebuild(y,"clean",myroot,0,edebug) |
| if retval: |
| print "!!! emerge aborting on ",y,"." |
| sys.exit(1) |
| #my doing an exit this way, --fetchonly can continue to try to |
| #fetch everything even if a particular download fails. |
| if "--fetchonly" in self.myopts: |
| if returnme: |
| print "\n\n!!! Some fetch errors were encountered. Please see above for details.\n\n" |
| sys.exit(returnme) |
| else: |
| sys.exit(0) |
| |
| def post_emerge(retval=0): |
| auxpat=re.compile('^([^-]*)(-\d+)?\.info(-\d+)?(\.gz)?') |
| global myopts |
| print |
| if "--pretend" in myopts: |
| sys.exit(retval) |
| root=portage.root |
| if not os.path.isdir(root+"usr/share/info"): |
| print " "+root+"usr/share/info doesn't exist, skipping info regeneration." |
| elif not os.path.exists("/usr/bin/install-info"): |
| print " /usr/bin/install-info doesn't exist; skipping info regeneration." |
| else: |
| print " "+green("*")+" Regenerating GNU info directory index..." |
| if os.path.exists(root+"usr/share/info/dir"): |
| os.rename(root+"usr/share/info/dir",root+"usr/share/info/dir.old") |
| icount=0 |
| badcount=0 |
| for x in os.listdir(root+"usr/share/info"): |
| aux=auxpat.search(x) |
| if not aux: |
| continue |
| auxgroups=aux.groups() |
| if not (auxgroups[1] or auxgroups[2]): |
| myso=commands.getstatusoutput("/usr/bin/install-info --dir-file="+root+"usr/share/info/dir "+root+"usr/share/info/"+x)[1] |
| if myso!="": |
| print auxgroups |
| badcount=badcount+1 |
| if "--verbose" in myopts: |
| print myso |
| icount=icount+1 |
| if badcount: |
| if "--verbose" not in myopts: |
| print " "+yellow("*")+" Processed",icount,"info files:",badcount,"errors; type "+green("emerge --verbose")+" to view errors." |
| else: |
| print " "+yellow("*")+" Processed",icount,"info files;",badcount,"errors." |
| |
| else: |
| print " "+green("*")+" Processed",icount,"info files." |
| if portage.settings["CONFIG_PROTECT"]: |
| #number of directories with some protect files in them |
| procount=0 |
| for x in string.split(portage.settings["CONFIG_PROTECT"]): |
| if os.path.isdir(x): |
| a=commands.getstatusoutput("cd "+x+"; find -iname '._cfg????_*'") |
| if a[0]!=0: |
| print " "+red("*")+" error scanning",x |
| else: |
| files=string.split(a[1]) |
| if files: |
| procount=procount+1 |
| print " "+yellow("* IMPORTANT:")+"",len(files),"config files in",x,"need updating." |
| if procount: |
| print " "+yellow("*")+" Type "+green("emerge --help config")+" to learn how to update config files." |
| print |
| sys.exit(retval) |
| |
| # general options that should be taken into account before any action |
| if not portage.settings.has_key("MAINTAINER_noclean"): |
| if not "--autoclean" in myopts: |
| myopts.append("--autoclean") |
| if "--debug" in myopts: |
| edebug=1 |
| |
| # process modes that aren't in the universal emerge interface format first |
| # this format is 'emerge --action --option --option [packageset]' |
| if "rsync" in mymode and not myaction: |
| if not os.path.exists("/usr/bin/rsync"): |
| print "!!! /usr/bin/rsync does not exist, so rsync support is disabled." |
| sys.exit(1) |
| rclean=0 |
| myportdir=portage.settings["PORTDIR"] |
| if myportdir[-1]=="/": |
| myportdir=myportdir[:-1] |
| if not os.path.exists(myportdir): |
| print ">>>",myportdir,"not found, creating it." |
| os.makedirs(myportdir,0755) |
| if "--clean" in myopts: |
| #we'll --delete files when we rsync |
| rclean=1 |
| mycommand="/usr/bin/rsync -rlptDv --stats --progress " |
| if rclean: |
| mycommand=mycommand+"--delete --exclude='distfiles/*' --exclude='packages/*' " |
| mycommand=mycommand+"rsync://cvs.gentoo.org/gentoo-x86-portage/* "+myportdir |
| print ">>> starting rsync with cvs.gentoo.org..." |
| #protect users that did not set a default umask |
| os.umask(022) |
| sys.exit(os.system(mycommand)) |
| |
| # HELP action |
| elif "--help"==myaction: |
| if len(mymode)==0: |
| print |
| print bold("Usage: ")+turquoise("emerge")+" [ "+green("action")+" ] [ "+green("options")+" ] [ "+turquoise("ebuildfile")+" | "+turquoise("tbz2file")+" | "+turquoise("dependency")+" ] ..." |
| print " "+turquoise("emerge")+" [ "+green("action")+" ] [ "+green("options")+" ] [ "+turquoise("system")+" | "+turquoise("world")+" | "+turquoise("noslot")+" ]" |
| print " "+turquoise("emerge")+" [ "+green("--clean")+" "+green("-c")+" ] "+turquoise("rsync") |
| print " "+turquoise("emerge")+" "+green("--help")+" "" "+green("-h")+" [ "+turquoise("rsync")+" | "+turquoise("system")+" | "+turquoise("world")+" | "+turquoise("noslot")+" | "+turquoise("config")+" ] " |
| print |
| print turquoise("Actions:") |
| print " "+green("--help")+" "+green("-h") |
| print " displays this help, an additional mode specifier provides" |
| print " detailed help about that mode instead" |
| print |
| print " "+green("--update")+" "+green("-u") |
| print " updates installed packages, this is typically used with",bold("world") |
| print " and",bold("system") |
| print |
| print " "+green("--zap")+" "+green("-z") |
| print " removes an installed package. the arguments can be in two" |
| print " different formats : /var/db/pkg/category/package-version" |
| print " or category/package." |
| print |
| print " "+green("--rebuild")+" "+green("-r") |
| print " forcably rebuild installed packages, this is typically used with" |
| print " "+bold("world"),"and",bold("system") |
| print |
| print " "+green("--search")+" "+green("-s") |
| print " searches for matches of the supplied string in the current local" |
| print " portage tree. The search string is a regular expression." |
| print " A few examples: " |
| print " "+bold("emerge --search \"^kde\"") |
| print " list all packages starting with kde" |
| print " "+bold("emerge --search \"gcc$\"") |
| print " list all packages ending with gcc" |
| print " "+bold("emerge --search \"\"")+" or" |
| print " "+bold("emerge --search \"*\"") |
| print " list all available packages " |
| print |
| print turquoise("Options:") |
| print " "+green("--autoclean")+" "+green("-a") |
| print " emerge normally cleans out the package-specific temporary" |
| print " build directory before it starts the building a package. With" |
| print " --autoclean, it will also clean the directory *after* the" |
| print " build completes. This option is automatically enabled for" |
| print " normal users, but maintainers can use this option to enable" |
| print " autocleaning." |
| print |
| print " "+green("--buildpkg")+" "+green("-b") |
| print " tell emerge to build binary packages for all ebuilds processed" |
| print " (in addition to actually merging the packages. Useful for" |
| print " maintainers or if you administrate multiple Gentoo Linux" |
| print " systems (build once, emerge tbz2s everywhere)." |
| print |
| print " "+green("--debug")+" "+green("-d") |
| print " Tell emerge to run the ebuild command in --debug mode. In this" |
| print " mode, the bash build environment will run with the -x option," |
| print " causing it to output verbose debug information print to stdout." |
| print " --debug is great for finding bash syntax errors." |
| print |
| print " "+green("--fetchonly")+" "+green("-f") |
| print " Instead of doing any package building, just perform fetches for" |
| print " all packages (main package as well as all dependencies.)" |
| print |
| print " "+green("--onlydeps")+" "+green("-o") |
| print " Only merge (or pretend to merge) the dependencies of the" |
| print " specified packages, not the packages themselves." |
| print |
| print " "+green("--pretend")+" "+green("-p") |
| print " instead of actually performing the merge, simply display what" |
| print " ebuilds and tbz2s *would* have been installed if --pretend" |
| print " weren't used. Using --pretend is strongly recommended before" |
| print " installing an unfamiliar package. In the printout, N = new," |
| print " U = upgrading, R = replacing" |
| print |
| print " "+green("--noreplace")+" "+green("-n") |
| print " Skip the packages specified on the command-line that have" |
| print " already been installed. Without this option, any packages," |
| print " ebuilds, or deps you specify on on the command-line *will* cause" |
| print " Portage to remerge the package, even if it is already installed." |
| print " Note that Portage will never remerge dependencies automatically." |
| print |
| print " "+green("--usepkg")+" "+green("-k") |
| print " tell emerge to use binary packages (from $PKGDIR) if they are" |
| print " available, thus possibly avoiding some time-consuming compiles." |
| print " This option is useful for CD installs; you can export" |
| print " PKGDIR=/mnt/cdrom/packages and then use this option to have" |
| print " emerge \"pull\" binary packages from the CD in order to satisfy" |
| print " dependencies." |
| print |
| print " "+green("--verbose")+" "+green("-v") |
| print " Tell emerge to run in verbose mode. Currently, this causes" |
| print " emerge to print out GNU info errors, if any." |
| print |
| elif "rsync" in mymode: |
| print |
| print bold("Usage: ")+turquoise("emerge")+" [ "+green("--clean")+" "+green("-c")+" ] "+turquoise("rsync") |
| print |
| print " \"emerge rsync\" initiates an rsync update with cvs.gentoo.org," |
| print " updating your Portage tree (typically in /usr/portage). This option" |
| print " will erase any changes that you have made to existing Portage files" |
| print " so be careful. \"emerge --clean rsync\" does the same thing as \"emerge" |
| print " rsync\", but files that no longer exist on our server are removed." |
| print |
| elif "system" in mymode: |
| print |
| print bold("Usage: ")+turquoise("emerge")+" [ "+green("action")+" ] "+" [ "+green("options")+" ] "+turquoise("system") |
| print |
| print " \"emerge system\" is the Portage system update command. When run, it" |
| print " will scan the etc/make.profile/packages file and determine what" |
| print " packages need to be installed so that your system meets the minimum" |
| print " requirements of your current system profile. Note that this doesn't" |
| print " necessarily bring your system up-to-date at all; instead, it just" |
| print " ensures that you have no missing parts. For example, if your system" |
| print " profile specifies that you should have sys-apps/iptables installed" |
| print " and you don't, then \"emerge system\" will install it (the most" |
| print " recent version that matches the profile spec) for you. It's always a" |
| print " good idea to do an \"emerge --pretend system\" before an \"emerge" |
| print " system\", just so you know what emerge is planning to do." |
| print |
| elif "world" in mymode: |
| print |
| print bold("Usage: ")+turquoise("emerge")+" [ "+green("action")+" ] "+" [ "+green("options")+" ] "+turquoise("world") |
| print |
| elif "noslot" in mymode: |
| print |
| print bold("Usage: ")+turquoise("emerge")+" [ "+green("action")+" ] "+" [ "+green("options")+" ] "+turquoise("noslot") |
| print |
| elif "config" in mymode: |
| outstuff=green("Config file management support (preliminary)")+""" |
| |
| Portage has a special feature called "config file protection". The purpose of |
| this feature is to prevent new package installs from clobbering existig |
| configuration files. By default, config file protection is turned on for /etc |
| and the KDE configuration dirs; more may be added in the future. |
| |
| When Portage installs a file into a protected directory tree like /etc, any |
| existing files will not be overwritten. If a file of the same name already |
| exists, Portage will change the name of the to-be- installed file from 'foo' to |
| '._cfg0000_foo'. If '._cfg0000_foo' already exists, this name becomes |
| '._cfg0001_foo', etc. In this way, existing files are not overwritten, |
| allowing the administrator to manually merge the new config files and avoid any |
| unexpected changes. |
| |
| In addition to protecting overwritten files, Portage will not delete any files |
| from a protected directory when a package is unmerged. While this may be a |
| little bit untidy, it does prevent potentially valuable config files from being |
| deleted, which is of paramount importance. |
| |
| Protected directories are set using the CONFIG_PROTECT variable, normally |
| defined in /etc/make.globals. Directory exceptions to the CONFIG_PROTECTed |
| directories can be specified using the CONFIG_PROTECT_MASK variable. To find |
| files that need to be updated in /etc, type: |
| |
| # find /etc -iname '._cfg????_*' |
| |
| You can disable this feature by setting CONFIG_PROTECT="" in /etc/make.conf. |
| Then, Portage will mercilessly auto-update your config files. Alternatively, |
| you can leave Config File Protection on but tell Portage that it can overwrite |
| files in certain specific /etc subdirectories. For example, if you wanted |
| Portage to automatically update your rc scripts and your wget configuration, |
| but didn't want any other changes made without your explicit approval, you'd |
| add this to /etc/make.conf: |
| |
| CONFIG_PROTECT_MASK="/etc/wget /etc/rc.d" |
| |
| """ |
| print outstuff |
| |
| # SEARCH action |
| elif "--search"==myaction: |
| if not myfiles: |
| print |
| print "No search terms provided." |
| print |
| else: |
| searchinstance = search() |
| for mysearch in myfiles: |
| searchinstance.setKey(mysearch) |
| searchinstance.execute() |
| searchinstance.output() |
| |
| # ZAP action |
| elif "--zap"==myaction: |
| if not myfiles and not "noslot" in mymode: |
| print |
| print "No packages to unmerge have been provided." |
| print |
| else: |
| full_paths={} |
| full_ebuild_paths={} |
| full_package_paths={} |
| non_slot_packages=[] |
| |
| var_path=portage.root+"var/db/pkg" |
| if "noslot" in mymode: |
| localtree=portage.vartree(portage.root) |
| for x in localtree.getallnodes(): |
| if localtree.slotted(x)==0: |
| mymatches=localtree.dep_match(x) |
| for package in mymatches: |
| myfullpaths=localtree.getebuildpaths(package) |
| for full_path in myfullpaths : |
| if not os.path.exists(full_path): |
| print "The ebuild '"+full_path+"' couldn't be found." |
| print "Your portage installation tree seems to be corrupted." |
| else: |
| full_package_paths[full_path]=package |
| else: |
| # process all arguments, verify if they can be resolved to an installed |
| # ebuild file and add the absolute paths of these ebuild to a list |
| for x in myfiles: |
| |
| # an absolute path has been given |
| if (x[0]=='/'): |
| path_parts=split(x, '/') |
| if len(path_parts) != 6: |
| print "When providing an absolute path it should be" |
| print "in the following format : "+var_path+"/category/package" |
| print "The path '"+x+"' doesn't respect this." |
| sys.exit(1) |
| elif not os.path.exists(x): |
| print "The path '"+x+"' doesn't exist." |
| sys.exit(1) |
| elif -1 == find(x, var_path): |
| print "The path '"+x+"' didn't commence with '"+var_path+"'." |
| sys.exit(1) |
| else: |
| # check if the SLOT file is present, and get the package version |
| # from the PF file in that case |
| package_slot_path=strip(x)+"/SLOT" |
| package_pf_path=strip(x)+"/PF" |
| if os.path.exists(package_pf_path): |
| package_pf_file=open(package_pf_path) |
| package_pf=package_pf_file.readline().strip() |
| package_pf_file.close() |
| else: |
| package_pf=path_parts[5] |
| full_path=strip(x)+"/"+package_pf+".ebuild" |
| if not os.path.exists(full_path): |
| print "The ebuild '"+full_path+"' couldn't be found." |
| print "Your portage installation tree seems to be corrupted." |
| else: |
| path_parts=split(full_path, "/") |
| full_ebuild_paths[full_path]=path_parts[4]+"/"+package_pf |
| |
| # a category and package name have been given |
| else: |
| localtree=portage.vartree(portage.root) |
| mymatches=localtree.dep_match(x) |
| # if no matches were found, try again by adding a '=' dep symbol if no |
| # dep symbols are already present |
| if not mymatches and x[0] not in ('<','>','=','~'): |
| mymatches=localtree.dep_match("="+x) |
| |
| # only loops over the matches if they were found |
| if mymatches: |
| for package in mymatches: |
| myfullpaths=localtree.getebuildpaths(package) |
| for full_path in myfullpaths : |
| if not os.path.exists(full_path): |
| print "The ebuild '"+full_path+"' couldn't be found." |
| print "Your portage installation tree seems to be corrupted." |
| else: |
| full_package_paths[full_path]=package |
| |
| # determine what the non slot packages are |
| if len(full_package_paths) > 0: |
| # determine the latest installed packages of each cat/pkg key |
| full_paths_keys=full_package_paths.keys() |
| # sort the ebuild paths |
| full_paths_keys.sort() |
| previous_package_set=None |
| for full_path in full_paths_keys: |
| # construct the key that will be used to determine when a new package set |
| # is being processed, the package slot is taken into account for this |
| package_parts=portage.pkgsplit(full_package_paths[full_path]) |
| slot_path=dirname(full_path)+"/SLOT" |
| if not os.path.exists(slot_path): |
| package_slot=package_parts[1] |
| non_slot_packages.append(package_parts[0]+"-"+package_parts[1]) |
| |
| full_paths=full_ebuild_paths.keys() |
| full_paths.extend(full_package_paths.keys()) |
| |
| if len(full_paths)==0: |
| print "Couldn't find any matching installed packages." |
| sys.exit(1) |
| else: |
| if "--pretend" in myopts: |
| print |
| print "The are the packages that I would unmerge :" |
| print |
| else: |
| print |
| print "The following packages are going to be unmerged :" |
| print |
| # this makes revisions come before slot versions |
| # and therefor unmerges the older packages first |
| full_paths.sort() |
| for full_path in full_paths: |
| path_parts=split(full_path, "/") |
| print " "+path_parts[4]+"/"+path_parts[5] |
| |
| if len(non_slot_packages)>0: |
| print |
| if "--verbose" not in myopts: |
| print " "+yellow("* IMPORTANT:"),len(non_slot_packages),"non slot packages; use "+green("--verbose")+" to view details." |
| else: |
| print " "+yellow("* IMPORTANT:"),len(non_slot_packages),"non slot packages" |
| print " "+yellow("*")+" These packages have been merged with an old version of portage that didn't" |
| print " "+yellow("*")+" support binary compatibility slots. This means that each package with a" |
| print " "+yellow("*")+" different version number will be considered different and only older versions" |
| print " "+yellow("*")+" of different releases will be unmerged automatically." |
| print " "+yellow("*")+" To get rid of all non slot packages, you can rebuild your system with" |
| print " "+yellow("*")+" "+green("emerge --noslot rebuild")+"." |
| print |
| for non_slot_package in non_slot_packages: |
| print " "+non_slot_package |
| |
| if "--pretend" in myopts: |
| print |
| else: |
| secs=string.atoi(portage.settings["UNMERGE_DELAY"]) |
| if secs > 0: |
| print |
| print "Waiting",secs,"seconds to make sure that you want these" |
| print "packages to be removed ..." |
| while secs > 0: |
| sys.stdout.write(str(secs)+" ") |
| sys.stdout.flush() |
| time.sleep(1) |
| secs = secs-1 |
| print secs |
| for full_path in full_paths: |
| print "Unmerging "+os.path.basename(full_path)+" ..." |
| retval=portage.doebuild(full_path,"unmerge",portage.root,0,edebug) |
| post_emerge() |
| |
| # REBUILD action |
| elif "--rebuild"==myaction: |
| if not "system" in mymode and not "world" in mymode and not "noslot" in mymode: |
| print |
| print " "+red("* WARNING:"),green("emerge --rebuild"),"shouldn't be used for regular packages" |
| print " "+red("*"),"it's only intended to be used with",green("system")+", "+green("world")+", and",green("noroot") |
| print |
| else: |
| if "--pretend" in myopts: |
| print |
| print "These are the packages that I would rebuild, in order." |
| print |
| |
| if "system" in mymode: |
| mydepgraph=depgraph(mymode,myaction,myopts) |
| print "Calculating system dependencies", |
| mydepgraph.syscreate(getsyslist()) |
| print " done!" |
| elif "world" in mymode or "noslot" in mymode: |
| mydepgraph=depgraph(mymode,myaction,myopts) |
| print "Calculating world dependencies", |
| mydepgraph.syscreate(mydepgraph.db["/"]["vartree"].getallnodes()) |
| print " done!" |
| |
| if "--pretend" in myopts: |
| mydepgraph.display(mydepgraph.altlist()) |
| |
| myoutdated=mydepgraph.outdated() |
| if len(myoutdated): |
| if "--verbose" not in myopts: |
| print |
| print " "+yellow("* IMPORTANT:"),len(myoutdated),"packages aren't available anymore; use "+green("--verbose")+" for details." |
| else: |
| print |
| print " "+yellow("* IMPORTANT:"),len(myoutdated),"packages aren't available anymore" |
| print " "+yellow("*")+" Your portage tree doesn't contain the ebuilds anymore that correspond to the" |
| print " "+yellow("*")+" following packages. This means that portage is unable to rebuild them." |
| print " "+yellow("*")+" Instead you should upgrade to the latest versions or install possible" |
| print " "+yellow("*")+" alternative packages that provide the same functionalities." |
| myoutdated.sort() |
| for mypackage in myoutdated: |
| print " "+mypackage |
| else: |
| mydepgraph.merge(mydepgraph.altlist()) |
| post_emerge() |
| |
| # INSTALL and UPDATE action |
| elif "--update"==myaction or not myaction: |
| if not myaction and "world" in mymode: |
| print |
| print " "+red("* WARNING:"),green("emerge world"),"can't be used without an action" |
| print " "+red("*"),green("world"),"is only intended to be used with",green("emerge --update") |
| print |
| else: |
| if "--pretend" in myopts: |
| print |
| print "These are the packages that I would merge, in order." |
| print |
| |
| if "system" in mymode: |
| mydepgraph=depgraph(mymode,myaction,myopts) |
| print "Calculating system dependencies", |
| mydepgraph.syscreate(getsyslist()) |
| print " done!" |
| elif "world" in mymode: |
| mydepgraph=depgraph(mymode,myaction,myopts) |
| print "Calculating world dependencies", |
| mydepgraph.syscreate(mydepgraph.db["/"]["vartree"].getallnodes()) |
| print " done!" |
| elif "noslot" in mymode: |
| mydepgraph=depgraph(mymode,myaction,myopts) |
| print "Calculating noslot dependencies", |
| mydepgraph.syscreate(mydepgraph.db["/"]["vartree"].getallnodes()) |
| print " done!" |
| else: |
| mydepgraph=depgraph(mymode,myaction,myopts) |
| if not myfiles: |
| post_emerge() |
| #we don't have any files to process; skip this step and exit |
| print "Calculating dependencies", |
| for mypkg in myfiles: |
| if mypkg[-5:]==".tbz2": |
| mytype="binary" |
| mytbz2=xpak.tbz2(mypkg) |
| mykey=mytbz2.getelements("CATEGORY")[0]+"/"+os.path.basename(mypkg)[:-5] |
| elif mypkg[-7:]==".ebuild": |
| mytype="ebuild" |
| mykey=os.path.basename(os.path.abspath(mypkg+"/../.."))+"/"+os.path.basename(mypkg)[:-7] |
| else: |
| mykey="" |
| if "--usepkg" in myopts: |
| mykey=mydepgraph.db[portage.root]["bintree"].dep_bestmatch(mypkg) |
| mytype="binary" |
| if not mykey: |
| mykey=mydepgraph.db[portage.root]["porttree"].dep_bestmatch(mypkg) |
| mytype="ebuild" |
| if not mykey: |
| print "!!! Couldn't find match for",mypkg+"; aborting." |
| sys.exit(1) |
| # else: |
| # print "!!! Error:",x,"is neither an ebuild nor a .tbz2 package." |
| # sys.exit(1) |
| mydepgraph.create(mytype+" "+portage.root+" "+mykey,None,"--onlydeps" not in myopts) |
| print " done!" |
| |
| if "--pretend" in myopts: |
| mydepgraph.display(mydepgraph.altlist()) |
| else: |
| mydepgraph.merge(mydepgraph.altlist()) |
| post_emerge() |
| |