| # portage.py -- core Portage functionality |
| # Copyright 1998-2004 Gentoo Foundation |
| # Distributed under the terms of the GNU General Public License v2 |
| # $Id: /var/cvsroot/gentoo-src/portage/pym/portage.py,v 1.524.2.76 2005/05/29 12:40:08 jstubbs Exp $ |
| |
| |
| VERSION="$Rev$"[6:-2] + "-svn" |
| |
| # =========================================================================== |
| # START OF IMPORTS -- START OF IMPORTS -- START OF IMPORTS -- START OF IMPORT |
| # =========================================================================== |
| |
| try: |
| import sys |
| except SystemExit, e: |
| raise |
| except: |
| print "Failed to import sys! Something is _VERY_ wrong with python." |
| raise SystemExit, 127 |
| |
| try: |
| import os,string,types,atexit,signal,fcntl |
| import time,cPickle,traceback,copy |
| import re,pwd,grp,commands |
| import shlex,shutil |
| |
| import stat |
| import commands |
| from time import sleep |
| from random import shuffle |
| except SystemExit, e: |
| raise |
| except Exception, e: |
| sys.stderr.write("\n\n") |
| sys.stderr.write("!!! Failed to complete python imports. There are internal modules for\n") |
| sys.stderr.write("!!! python and failure here indicates that you have a problem with python\n") |
| sys.stderr.write("!!! itself and thus portage is not able to continue processing.\n\n") |
| |
| sys.stderr.write("!!! You might consider starting python with verbose flags to see what has\n") |
| sys.stderr.write("!!! gone wrong. Here is the information we got for this exception:\n") |
| |
| sys.stderr.write(" "+str(e)+"\n\n"); |
| sys.exit(127) |
| except: |
| sys.stderr.write("\n\n") |
| sys.stderr.write("!!! Failed to complete python imports. There are internal modules for\n") |
| sys.stderr.write("!!! python and failure here indicates that you have a problem with python\n") |
| sys.stderr.write("!!! itself and thus portage is not able to continue processing.\n\n") |
| |
| sys.stderr.write("!!! You might consider starting python with verbose flags to see what has\n") |
| sys.stderr.write("!!! gone wrong. The exception was non-standard and we were unable to catch it.\n\n") |
| sys.exit(127) |
| |
| try: |
| # XXX: This should get renamed to bsd_chflags, I think. |
| import chflags |
| bsd_chflags = chflags |
| except SystemExit, e: |
| raise |
| except: |
| # XXX: This should get renamed to bsd_chflags, I think. |
| bsd_chflags = None |
| |
| try: |
| import cvstree |
| import xpak |
| import getbinpkg |
| import portage_dep |
| |
| # XXX: This needs to get cleaned up. |
| import output |
| from output import blue, bold, brown, darkblue, darkgreen, darkred, darkteal, \ |
| darkyellow, fuchsia, fuscia, green, purple, red, teal, turquoise, white, \ |
| xtermTitle, xtermTitleReset, yellow |
| |
| import portage_const |
| from portage_const import VDB_PATH, PRIVATE_PATH, CACHE_PATH, DEPCACHE_PATH, \ |
| USER_CONFIG_PATH, MODULES_FILE_PATH, CUSTOM_PROFILE_PATH, PORTAGE_BASE_PATH, \ |
| PORTAGE_BIN_PATH, PORTAGE_PYM_PATH, PROFILE_PATH, LOCALE_DATA_PATH, \ |
| EBUILD_SH_BINARY, SANDBOX_BINARY, BASH_BINARY, \ |
| MOVE_BINARY, PRELINK_BINARY, WORLD_FILE, MAKE_CONF_FILE, MAKE_DEFAULTS_FILE, \ |
| DEPRECATED_PROFILE_FILE, USER_VIRTUALS_FILE, EBUILD_SH_ENV_FILE, \ |
| INVALID_ENV_FILE, CUSTOM_MIRRORS_FILE, SANDBOX_PIDS_FILE, CONFIG_MEMORY_FILE,\ |
| INCREMENTALS, STICKIES, EAPI |
| |
| from portage_data import ostype, lchown, userland, secpass, uid, wheelgid, \ |
| portage_uid, portage_gid |
| |
| import portage_util |
| from portage_util import grab_multiple, grabdict, grabdict_package, grabfile, grabfile_package, \ |
| grabints, map_dictlist_vals, pickle_read, pickle_write, stack_dictlist, stack_dicts, stack_lists, \ |
| unique_array, varexpand, writedict, writeints, writemsg, getconfig |
| import portage_exception |
| import portage_gpg |
| import portage_locks |
| import portage_exec |
| from portage_locks import unlockfile,unlockdir,lockfile,lockdir |
| import portage_checksum |
| from portage_checksum import perform_md5,perform_checksum,prelink_capable |
| from portage_localization import _ |
| except SystemExit, e: |
| raise |
| except Exception, e: |
| sys.stderr.write("\n\n") |
| sys.stderr.write("!!! Failed to complete portage imports. There are internal modules for\n") |
| sys.stderr.write("!!! portage and failure here indicates that you have a problem with your\n") |
| sys.stderr.write("!!! installation of portage. Please try a rescue portage located in the\n") |
| sys.stderr.write("!!! portage tree under '/usr/portage/sys-apps/portage/files/' (default).\n") |
| sys.stderr.write("!!! There is a README.RESCUE file that details the steps required to perform\n") |
| sys.stderr.write("!!! a recovery of portage.\n") |
| |
| sys.stderr.write(" "+str(e)+"\n\n") |
| sys.exit(127) |
| |
| |
| # =========================================================================== |
| # END OF IMPORTS -- END OF IMPORTS -- END OF IMPORTS -- END OF IMPORTS -- END |
| # =========================================================================== |
| |
| |
| def exithandler(signum,frame): |
| """Handles ^C interrupts in a sane manner""" |
| signal.signal(signal.SIGINT, signal.SIG_IGN) |
| signal.signal(signal.SIGTERM, signal.SIG_IGN) |
| |
| # 0=send to *everybody* in process group |
| portageexit() |
| print "Exiting due to signal" |
| os.kill(0,signum) |
| sys.exit(1) |
| |
| signal.signal(signal.SIGCHLD, signal.SIG_DFL) |
| signal.signal(signal.SIGINT, exithandler) |
| signal.signal(signal.SIGTERM, exithandler) |
| signal.signal(signal.SIGPIPE, signal.SIG_DFL) |
| |
| def load_mod(name): |
| modname = string.join(string.split(name,".")[:-1],".") |
| mod = __import__(modname) |
| components = name.split('.') |
| for comp in components[1:]: |
| mod = getattr(mod, comp) |
| return mod |
| |
| def best_from_dict(key, top_dict, key_order, EmptyOnError=1, FullCopy=1, AllowEmpty=1): |
| for x in key_order: |
| if top_dict.has_key(x) and top_dict[x].has_key(key): |
| if FullCopy: |
| return copy.deepcopy(top_dict[x][key]) |
| else: |
| return top_dict[x][key] |
| if EmptyOnError: |
| return "" |
| else: |
| raise KeyError, "Key not found in list; '%s'" % key |
| |
| def getcwd(): |
| "this fixes situations where the current directory doesn't exist" |
| try: |
| return os.getcwd() |
| except SystemExit, e: |
| raise |
| except: |
| os.chdir("/") |
| return "/" |
| getcwd() |
| |
| def abssymlink(symlink): |
| "This reads symlinks, resolving the relative symlinks, and returning the absolute." |
| mylink=os.readlink(symlink) |
| if mylink[0] != '/': |
| mydir=os.path.dirname(symlink) |
| mylink=mydir+"/"+mylink |
| return os.path.normpath(mylink) |
| |
| def suffix_array(array,suffix,doblanks=1): |
| """Appends a given suffix to each element in an Array/List/Tuple. |
| Returns a List.""" |
| if type(array) not in [types.ListType, types.TupleType]: |
| raise TypeError, "List or Tuple expected. Got %s" % type(array) |
| newarray=[] |
| for x in array: |
| if x or doblanks: |
| newarray.append(x + suffix) |
| else: |
| newarray.append(x) |
| return newarray |
| |
| def prefix_array(array,prefix,doblanks=1): |
| """Prepends a given prefix to each element in an Array/List/Tuple. |
| Returns a List.""" |
| if type(array) not in [types.ListType, types.TupleType]: |
| raise TypeError, "List or Tuple expected. Got %s" % type(array) |
| newarray=[] |
| for x in array: |
| if x or doblanks: |
| newarray.append(prefix + x) |
| else: |
| newarray.append(x) |
| return newarray |
| |
| def normalize_path(mypath): |
| newpath = os.path.normpath(mypath) |
| if len(newpath) > 1: |
| if newpath[:2] == "//": |
| newpath = newpath[1:] |
| return newpath |
| |
| dircache = {} |
| cacheHit=0 |
| cacheMiss=0 |
| cacheStale=0 |
| def cacheddir(my_original_path, ignorecvs, ignorelist, EmptyOnError, followSymlinks=True): |
| global cacheHit,cacheMiss,cacheStale |
| mypath = normalize_path(my_original_path) |
| if dircache.has_key(mypath): |
| cacheHit += 1 |
| cached_mtime, list, ftype = dircache[mypath] |
| else: |
| cacheMiss += 1 |
| cached_mtime, list, ftype = -1, [], [] |
| try: |
| pathstat = os.stat(mypath) |
| if stat.S_ISDIR(pathstat[stat.ST_MODE]): |
| mtime = pathstat[stat.ST_MTIME] |
| else: |
| raise Exception |
| except SystemExit, e: |
| raise |
| except: |
| if EmptyOnError: |
| return [], [] |
| return None, None |
| # Python retuns mtime in seconds, so if it was changed in the last few seconds, it could be invalid |
| if mtime != cached_mtime or time.time() - mtime < 4: |
| if dircache.has_key(mypath): |
| cacheStale += 1 |
| list = os.listdir(mypath) |
| ftype = [] |
| for x in list: |
| try: |
| if followSymlinks: |
| pathstat = os.stat(mypath+"/"+x) |
| else: |
| pathstat = os.lstat(mypath+"/"+x) |
| |
| if stat.S_ISREG(pathstat[stat.ST_MODE]): |
| ftype.append(0) |
| elif stat.S_ISDIR(pathstat[stat.ST_MODE]): |
| ftype.append(1) |
| elif stat.S_ISLNK(pathstat[stat.ST_MODE]): |
| ftype.append(2) |
| else: |
| ftype.append(3) |
| except SystemExit, e: |
| raise |
| except: |
| ftype.append(3) |
| dircache[mypath] = mtime, list, ftype |
| |
| ret_list = [] |
| ret_ftype = [] |
| for x in range(0, len(list)): |
| if(ignorecvs and (len(list[x]) > 2) and (list[x][:2]!=".#")): |
| ret_list.append(list[x]) |
| ret_ftype.append(ftype[x]) |
| elif (list[x] not in ignorelist): |
| ret_list.append(list[x]) |
| ret_ftype.append(ftype[x]) |
| |
| writemsg("cacheddirStats: H:%d/M:%d/S:%d\n" % (cacheHit, cacheMiss, cacheStale),10) |
| return ret_list, ret_ftype |
| |
| |
| def listdir(mypath, recursive=False, filesonly=False, ignorecvs=False, ignorelist=[], followSymlinks=True, |
| EmptyOnError=False, dirsonly=False): |
| |
| list, ftype = cacheddir(mypath, ignorecvs, ignorelist, EmptyOnError, followSymlinks) |
| |
| if list is None: |
| list=[] |
| if ftype is None: |
| ftype=[] |
| |
| if not filesonly and not recursive: |
| return list |
| |
| if recursive: |
| x=0 |
| while x<len(ftype): |
| if ftype[x]==1 and not (ignorecvs and os.path.basename(list[x]) in ('CVS','.svn','SCCS')): |
| l,f = cacheddir(mypath+"/"+list[x], ignorecvs, ignorelist, EmptyOnError, |
| followSymlinks) |
| |
| l=l[:] |
| for y in range(0,len(l)): |
| l[y]=list[x]+"/"+l[y] |
| list=list+l |
| ftype=ftype+f |
| x+=1 |
| if filesonly: |
| rlist=[] |
| for x in range(0,len(ftype)): |
| if ftype[x]==0: |
| rlist=rlist+[list[x]] |
| elif dirsonly: |
| rlist = [] |
| for x in range(0, len(ftype)): |
| if ftype[x] == 1: |
| rlist = rlist + [list[x]] |
| else: |
| rlist=list |
| |
| return rlist |
| |
| starttime=long(time.time()) |
| features=[] |
| |
| def tokenize(mystring): |
| """breaks a string like 'foo? (bar) oni? (blah (blah))' |
| into embedded lists; returns None on paren mismatch""" |
| |
| # This function is obsoleted. |
| # Use dep_parenreduce |
| |
| newtokens=[] |
| curlist=newtokens |
| prevlists=[] |
| level=0 |
| accum="" |
| for x in mystring: |
| if x=="(": |
| if accum: |
| curlist.append(accum) |
| accum="" |
| prevlists.append(curlist) |
| curlist=[] |
| level=level+1 |
| elif x==")": |
| if accum: |
| curlist.append(accum) |
| accum="" |
| if level==0: |
| writemsg("!!! tokenizer: Unmatched left parenthesis in:\n'"+str(mystring)+"'\n") |
| return None |
| newlist=curlist |
| curlist=prevlists.pop() |
| curlist.append(newlist) |
| level=level-1 |
| elif x in string.whitespace: |
| if accum: |
| curlist.append(accum) |
| accum="" |
| else: |
| accum=accum+x |
| if accum: |
| curlist.append(accum) |
| if (level!=0): |
| writemsg("!!! tokenizer: Exiting with unterminated parenthesis in:\n'"+str(mystring)+"'\n") |
| return None |
| return newtokens |
| |
| def flatten(mytokens): |
| """this function now turns a [1,[2,3]] list into |
| a [1,2,3] list and returns it.""" |
| newlist=[] |
| for x in mytokens: |
| if type(x)==types.ListType: |
| newlist.extend(flatten(x)) |
| else: |
| newlist.append(x) |
| return newlist |
| |
| #beautiful directed graph object |
| |
| class digraph: |
| def __init__(self): |
| self.dict={} |
| #okeys = keys, in order they were added (to optimize firstzero() ordering) |
| self.okeys=[] |
| |
| def addnode(self,mykey,myparent): |
| if not self.dict.has_key(mykey): |
| self.okeys.append(mykey) |
| if myparent==None: |
| self.dict[mykey]=[0,[]] |
| else: |
| self.dict[mykey]=[0,[myparent]] |
| self.dict[myparent][0]=self.dict[myparent][0]+1 |
| return |
| if myparent and (not myparent in self.dict[mykey][1]): |
| self.dict[mykey][1].append(myparent) |
| self.dict[myparent][0]=self.dict[myparent][0]+1 |
| |
| def delnode(self,mykey): |
| if not self.dict.has_key(mykey): |
| return |
| for x in self.dict[mykey][1]: |
| self.dict[x][0]=self.dict[x][0]-1 |
| del self.dict[mykey] |
| while 1: |
| try: |
| self.okeys.remove(mykey) |
| except ValueError: |
| break |
| |
| def allnodes(self): |
| "returns all nodes in the dictionary" |
| return self.dict.keys() |
| |
| def firstzero(self): |
| "returns first node with zero references, or NULL if no such node exists" |
| for x in self.okeys: |
| if self.dict[x][0]==0: |
| return x |
| return None |
| |
| def depth(self, mykey): |
| depth=0 |
| while (self.dict[mykey][1]): |
| depth=depth+1 |
| mykey=self.dict[mykey][1][0] |
| return depth |
| |
| def allzeros(self): |
| "returns all nodes with zero references, or NULL if no such node exists" |
| zerolist = [] |
| for x in self.dict.keys(): |
| mys = string.split(x) |
| if mys[0] != "blocks" and self.dict[x][0]==0: |
| zerolist.append(x) |
| return zerolist |
| |
| def hasallzeros(self): |
| "returns 0/1, Are all nodes zeros? 1 : 0" |
| zerolist = [] |
| for x in self.dict.keys(): |
| if self.dict[x][0]!=0: |
| return 0 |
| return 1 |
| |
| def empty(self): |
| if len(self.dict)==0: |
| return 1 |
| return 0 |
| |
| def hasnode(self,mynode): |
| return self.dict.has_key(mynode) |
| |
| def copy(self): |
| mygraph=digraph() |
| for x in self.dict.keys(): |
| mygraph.dict[x]=self.dict[x][:] |
| mygraph.okeys=self.okeys[:] |
| return mygraph |
| |
| # valid end of version components; integers specify offset from release version |
| # pre=prerelease, p=patchlevel (should always be followed by an int), rc=release candidate |
| # all but _p (where it is required) can be followed by an optional trailing integer |
| |
| endversion={"pre":-2,"p":0,"alpha":-4,"beta":-3,"rc":-1} |
| # as there's no reliable way to set {}.keys() order |
| # netversion_keys will be used instead of endversion.keys |
| # to have fixed search order, so that "pre" is checked |
| # before "p" |
| endversion_keys = ["pre", "p", "alpha", "beta", "rc"] |
| |
| #parse /etc/env.d and generate /etc/profile.env |
| |
| def env_update(makelinks=1): |
| global root |
| if not os.path.exists(root+"etc/env.d"): |
| prevmask=os.umask(0) |
| os.makedirs(root+"etc/env.d",0755) |
| os.umask(prevmask) |
| fns=listdir(root+"etc/env.d",EmptyOnError=1) |
| fns.sort() |
| pos=0 |
| while (pos<len(fns)): |
| if len(fns[pos])<=2: |
| del fns[pos] |
| continue |
| if (fns[pos][0] not in string.digits) or (fns[pos][1] not in string.digits): |
| del fns[pos] |
| continue |
| pos=pos+1 |
| |
| specials={ |
| "KDEDIRS":[],"PATH":[],"CLASSPATH":[],"LDPATH":[],"MANPATH":[], |
| "INFODIR":[],"INFOPATH":[],"ROOTPATH":[],"CONFIG_PROTECT":[], |
| "CONFIG_PROTECT_MASK":[],"PRELINK_PATH":[],"PRELINK_PATH_MASK":[], |
| "PYTHONPATH":[], "ADA_INCLUDE_PATH":[], "ADA_OBJECTS_PATH":[] |
| } |
| colon_separated = [ |
| "ADA_INCLUDE_PATH", "ADA_OBJECTS_PATH", |
| "LDPATH", "MANPATH", |
| "PATH", "PRELINK_PATH", |
| "PRELINK_PATH_MASK", "PYTHONPATH" |
| ] |
| |
| env={} |
| |
| for x in fns: |
| # don't process backup files |
| if x[-1]=='~' or x[-4:]==".bak": |
| continue |
| myconfig=getconfig(root+"etc/env.d/"+x) |
| if myconfig==None: |
| writemsg("!!! Parsing error in "+str(root)+"etc/env.d/"+str(x)+"\n") |
| #parse error |
| continue |
| # process PATH, CLASSPATH, LDPATH |
| for myspec in specials.keys(): |
| if myconfig.has_key(myspec): |
| if myspec in colon_separated: |
| specials[myspec].extend(myconfig[myspec].split(":")) |
| else: |
| specials[myspec].append(myconfig[myspec]) |
| del myconfig[myspec] |
| # process all other variables |
| for myenv in myconfig.keys(): |
| env[myenv]=myconfig[myenv] |
| |
| if os.path.exists(root+"etc/ld.so.conf"): |
| myld=open(root+"etc/ld.so.conf") |
| myldlines=myld.readlines() |
| myld.close() |
| oldld=[] |
| for x in myldlines: |
| #each line has at least one char (a newline) |
| if x[0]=="#": |
| continue |
| oldld.append(x[:-1]) |
| # os.rename(root+"etc/ld.so.conf",root+"etc/ld.so.conf.bak") |
| # Where is the new ld.so.conf generated? (achim) |
| else: |
| oldld=None |
| |
| ld_cache_update=False |
| if os.environ.has_key("PORTAGE_CALLER") and \ |
| os.environ["PORTAGE_CALLER"] == "env-update": |
| ld_cache_update = True |
| |
| newld=specials["LDPATH"] |
| if (oldld!=newld): |
| #ld.so.conf needs updating and ldconfig needs to be run |
| myfd=open(root+"etc/ld.so.conf","w") |
| myfd.write("# ld.so.conf autogenerated by env-update; make all changes to\n") |
| myfd.write("# contents of /etc/env.d directory\n") |
| for x in specials["LDPATH"]: |
| myfd.write(x+"\n") |
| myfd.close() |
| ld_cache_update=True |
| |
| # Update prelink.conf if we are prelink-enabled |
| if prelink_capable: |
| newprelink=open(root+"etc/prelink.conf","w") |
| newprelink.write("# prelink.conf autogenerated by env-update; make all changes to\n") |
| newprelink.write("# contents of /etc/env.d directory\n") |
| |
| for x in ["/bin","/sbin","/usr/bin","/usr/sbin","/lib","/usr/lib"]: |
| newprelink.write("-l "+x+"\n"); |
| for x in specials["LDPATH"]+specials["PATH"]+specials["PRELINK_PATH"]: |
| if not x: |
| continue |
| if x[-1]!='/': |
| x=x+"/" |
| plmasked=0 |
| for y in specials["PRELINK_PATH_MASK"]: |
| if not y: |
| continue |
| if y[-1]!='/': |
| y=y+"/" |
| if y==x[0:len(y)]: |
| plmasked=1 |
| break |
| if not plmasked: |
| newprelink.write("-h "+x+"\n") |
| for x in specials["PRELINK_PATH_MASK"]: |
| newprelink.write("-b "+x+"\n") |
| newprelink.close() |
| |
| if not mtimedb.has_key("ldpath"): |
| mtimedb["ldpath"]={} |
| |
| for x in specials["LDPATH"]+['/usr/lib','/lib']: |
| try: |
| newldpathtime=os.stat(x)[stat.ST_MTIME] |
| except SystemExit, e: |
| raise |
| except: |
| newldpathtime=0 |
| if mtimedb["ldpath"].has_key(x): |
| if mtimedb["ldpath"][x]==newldpathtime: |
| pass |
| else: |
| mtimedb["ldpath"][x]=newldpathtime |
| ld_cache_update=True |
| else: |
| mtimedb["ldpath"][x]=newldpathtime |
| ld_cache_update=True |
| |
| # ldconfig has very different behaviour between FreeBSD and Linux |
| if ostype=="Linux" or ostype.lower().endswith("gnu"): |
| if (ld_cache_update or makelinks): |
| # We can't update links if we haven't cleaned other versions first, as |
| # an older package installed ON TOP of a newer version will cause ldconfig |
| # to overwrite the symlinks we just made. -X means no links. After 'clean' |
| # we can safely create links. |
| writemsg(">>> Regenerating "+str(root)+"etc/ld.so.cache...\n") |
| if makelinks: |
| commands.getstatusoutput("cd / ; /sbin/ldconfig -r "+root) |
| else: |
| commands.getstatusoutput("cd / ; /sbin/ldconfig -X -r "+root) |
| elif ostype == "FreeBSD": |
| if (ld_cache_update): |
| writemsg(">>> Regenerating "+str(root)+"var/run/ld-elf.so.hints...\n") |
| commands.getstatusoutput("cd / ; /sbin/ldconfig -i -elf -f "+str(root)+"var/run/ld-elf.so.hints "+str(root)+"etc/ld.so.conf") |
| |
| del specials["LDPATH"] |
| |
| penvnotice = "# THIS FILE IS AUTOMATICALLY GENERATED BY env-update.\n" |
| penvnotice += "# DO NOT EDIT THIS FILE. CHANGES TO STARTUP PROFILES\n" |
| cenvnotice = penvnotice[:] |
| penvnotice += "# GO INTO /etc/profile NOT /etc/profile.env\n\n" |
| cenvnotice += "# GO INTO /etc/csh.cshrc NOT /etc/csh.env\n\n" |
| |
| #create /etc/profile.env for bash support |
| outfile=open(root+"/etc/profile.env","w") |
| outfile.write(penvnotice) |
| |
| for path in specials.keys(): |
| if len(specials[path])==0: |
| continue |
| outstring="export "+path+"='" |
| if path in ["CONFIG_PROTECT","CONFIG_PROTECT_MASK"]: |
| for x in specials[path][:-1]: |
| outstring += x+" " |
| else: |
| for x in specials[path][:-1]: |
| outstring=outstring+x+":" |
| outstring=outstring+specials[path][-1]+"'" |
| outfile.write(outstring+"\n") |
| |
| #create /etc/profile.env |
| for x in env.keys(): |
| if type(env[x])!=types.StringType: |
| continue |
| outfile.write("export "+x+"='"+env[x]+"'\n") |
| outfile.close() |
| |
| #create /etc/csh.env for (t)csh support |
| outfile=open(root+"/etc/csh.env","w") |
| outfile.write(cenvnotice) |
| |
| for path in specials.keys(): |
| if len(specials[path])==0: |
| continue |
| outstring="setenv "+path+" '" |
| if path in ["CONFIG_PROTECT","CONFIG_PROTECT_MASK"]: |
| for x in specials[path][:-1]: |
| outstring += x+" " |
| else: |
| for x in specials[path][:-1]: |
| outstring=outstring+x+":" |
| outstring=outstring+specials[path][-1]+"'" |
| outfile.write(outstring+"\n") |
| #get it out of the way |
| del specials[path] |
| |
| #create /etc/csh.env |
| for x in env.keys(): |
| if type(env[x])!=types.StringType: |
| continue |
| outfile.write("setenv "+x+" '"+env[x]+"'\n") |
| outfile.close() |
| |
| def new_protect_filename(mydest, newmd5=None): |
| """Resolves a config-protect filename for merging, optionally |
| using the last filename if the md5 matches. |
| (dest,md5) ==> 'string' --- path_to_target_filename |
| (dest) ==> ('next', 'highest') --- next_target and most-recent_target |
| """ |
| |
| # config protection filename format: |
| # ._cfg0000_foo |
| # 0123456789012 |
| prot_num=-1 |
| last_pfile="" |
| |
| if (len(mydest) == 0): |
| raise ValueError, "Empty path provided where a filename is required" |
| if (mydest[-1]=="/"): # XXX add better directory checking |
| raise ValueError, "Directory provided but this function requires a filename" |
| if not os.path.exists(mydest): |
| return mydest |
| |
| real_filename = os.path.basename(mydest) |
| real_dirname = os.path.dirname(mydest) |
| for pfile in listdir(real_dirname): |
| if pfile[0:5] != "._cfg": |
| continue |
| if pfile[10:] != real_filename: |
| continue |
| try: |
| new_prot_num = int(pfile[5:9]) |
| if new_prot_num > prot_num: |
| prot_num = new_prot_num |
| last_pfile = pfile |
| except SystemExit, e: |
| raise |
| except: |
| continue |
| prot_num = prot_num + 1 |
| |
| new_pfile = os.path.normpath(real_dirname+"/._cfg"+string.zfill(prot_num,4)+"_"+real_filename) |
| old_pfile = os.path.normpath(real_dirname+"/"+last_pfile) |
| if last_pfile and newmd5: |
| if portage_checksum.perform_md5(real_dirname+"/"+last_pfile) == newmd5: |
| return old_pfile |
| else: |
| return new_pfile |
| elif newmd5: |
| return new_pfile |
| else: |
| return (new_pfile, old_pfile) |
| |
| #XXX: These two are now implemented in portage_util.py but are needed here |
| #XXX: until the isvalidatom() dependency is sorted out. |
| |
| def grabdict_package(myfilename,juststrings=0): |
| pkgs=grabdict(myfilename, juststrings=juststrings, empty=1) |
| for x in pkgs.keys(): |
| if not isvalidatom(x): |
| del(pkgs[x]) |
| writemsg("--- Invalid atom in %s: %s\n" % (myfilename, x)) |
| return pkgs |
| |
| def grabfile_package(myfilename,compatlevel=0): |
| pkgs=grabfile(myfilename,compatlevel) |
| for x in range(len(pkgs)-1,-1,-1): |
| pkg = pkgs[x] |
| if pkg[0] == "-": |
| pkg = pkg[1:] |
| if pkg[0] == "*": |
| pkg = pkg[1:] |
| if not isvalidatom(pkg): |
| writemsg("--- Invalid atom in %s: %s\n" % (myfilename, pkgs[x])) |
| del(pkgs[x]) |
| return pkgs |
| |
| # returns a tuple. (version[string], error[string]) |
| # They are pretty much mutually exclusive. |
| # Either version is a string and error is none, or |
| # version is None and error is a string |
| # |
| def ExtractKernelVersion(base_dir): |
| lines = [] |
| pathname = os.path.join(base_dir, 'Makefile') |
| try: |
| f = open(pathname, 'r') |
| except OSError, details: |
| return (None, str(details)) |
| except IOError, details: |
| return (None, str(details)) |
| |
| try: |
| for i in range(4): |
| lines.append(f.readline()) |
| except OSError, details: |
| return (None, str(details)) |
| except IOError, details: |
| return (None, str(details)) |
| |
| lines = map(string.strip, lines) |
| |
| version = '' |
| |
| #XXX: The following code relies on the ordering of vars within the Makefile |
| for line in lines: |
| # split on the '=' then remove annoying whitespace |
| items = string.split(line, '=') |
| items = map(string.strip, items) |
| if items[0] == 'VERSION' or \ |
| items[0] == 'PATCHLEVEL': |
| version += items[1] |
| version += "." |
| elif items[0] == 'SUBLEVEL': |
| version += items[1] |
| elif items[0] == 'EXTRAVERSION' and \ |
| items[-1] != items[0]: |
| version += items[1] |
| |
| # Grab a list of files named localversion* and sort them |
| localversions = os.listdir(base_dir) |
| for x in range(len(localversions)-1,-1,-1): |
| if localversions[x][:12] != "localversion": |
| del localversions[x] |
| localversions.sort() |
| |
| # Append the contents of each to the version string, stripping ALL whitespace |
| for lv in localversions: |
| version += string.join(string.split(string.join(grabfile(base_dir+"/"+lv))), "") |
| |
| # Check the .config for a CONFIG_LOCALVERSION and append that too, also stripping whitespace |
| kernelconfig = getconfig(base_dir+"/.config") |
| if kernelconfig and kernelconfig.has_key("CONFIG_LOCALVERSION"): |
| version += string.join(string.split(kernelconfig["CONFIG_LOCALVERSION"]), "") |
| |
| return (version,None) |
| |
| |
| autouse_val = None |
| def autouse(myvartree,use_cache=1): |
| "returns set of USE variables auto-enabled due to packages being installed" |
| global usedefaults, autouse_val |
| if autouse_val is not None: |
| return autouse_val |
| if profiledir==None: |
| autouse_val = "" |
| return "" |
| myusevars="" |
| for myuse in usedefaults: |
| dep_met = True |
| for mydep in usedefaults[myuse]: |
| if not myvartree.dep_match(mydep,use_cache=True): |
| dep_met = False |
| break |
| if dep_met: |
| myusevars += " "+myuse |
| autouse_val = myusevars |
| return myusevars |
| |
| def check_config_instance(test): |
| if not test or (str(test.__class__) != 'portage.config'): |
| raise TypeError, "Invalid type for config object: %s" % test.__class__ |
| |
| class config: |
| def __init__(self, clone=None, mycpv=None, config_profile_path=None, config_incrementals=None): |
| |
| self.already_in_regenerate = 0 |
| |
| self.locked = 0 |
| self.mycpv = None |
| self.puse = [] |
| self.modifiedkeys = [] |
| |
| self.virtuals = {} |
| self.v_count = 0 |
| |
| # Virtuals obtained from the vartree |
| self.treeVirtuals = {} |
| # Virtuals by user specification. Includes negatives. |
| self.userVirtuals = {} |
| # Virtual negatives from user specifications. |
| self.negVirtuals = {} |
| |
| self.user_profile_dir = None |
| |
| if clone: |
| self.incrementals = copy.deepcopy(clone.incrementals) |
| self.profile_path = copy.deepcopy(clone.profile_path) |
| self.user_profile_dir = copy.deepcopy(clone.user_profile_dir) |
| |
| self.module_priority = copy.deepcopy(clone.module_priority) |
| self.modules = copy.deepcopy(clone.modules) |
| |
| self.depcachedir = copy.deepcopy(clone.depcachedir) |
| |
| self.packages = copy.deepcopy(clone.packages) |
| self.virtuals = copy.deepcopy(clone.virtuals) |
| |
| self.treeVirtuals = copy.deepcopy(clone.treeVirtuals) |
| self.userVirtuals = copy.deepcopy(clone.userVirtuals) |
| self.negVirtuals = copy.deepcopy(clone.negVirtuals) |
| |
| self.use_defs = copy.deepcopy(clone.use_defs) |
| self.usemask = copy.deepcopy(clone.usemask) |
| |
| self.configlist = copy.deepcopy(clone.configlist) |
| self.configlist[-1] = os.environ.copy() |
| self.configdict = { "globals": self.configlist[0], |
| "defaults": self.configlist[1], |
| "conf": self.configlist[2], |
| "pkg": self.configlist[3], |
| "auto": self.configlist[4], |
| "backupenv": self.configlist[5], |
| "env": self.configlist[6] } |
| self.profiles = copy.deepcopy(clone.profiles) |
| self.backupenv = copy.deepcopy(clone.backupenv) |
| self.pusedict = copy.deepcopy(clone.pusedict) |
| self.categories = copy.deepcopy(clone.categories) |
| self.pkeywordsdict = copy.deepcopy(clone.pkeywordsdict) |
| self.pmaskdict = copy.deepcopy(clone.pmaskdict) |
| self.punmaskdict = copy.deepcopy(clone.punmaskdict) |
| self.prevmaskdict = copy.deepcopy(clone.prevmaskdict) |
| self.pprovideddict = copy.deepcopy(clone.pprovideddict) |
| self.lookuplist = copy.deepcopy(clone.lookuplist) |
| self.uvlist = copy.deepcopy(clone.uvlist) |
| self.dirVirtuals = copy.deepcopy(clone.dirVirtuals) |
| self.treeVirtuals = copy.deepcopy(clone.treeVirtuals) |
| else: |
| self.depcachedir = DEPCACHE_PATH |
| |
| if not config_profile_path: |
| global profiledir |
| writemsg("config_profile_path not specified to class config\n") |
| self.profile_path = profiledir[:] |
| else: |
| self.profile_path = config_profile_path[:] |
| |
| if not config_incrementals: |
| writemsg("incrementals not specified to class config\n") |
| self.incrementals = copy.deepcopy(portage_const.INCREMENTALS) |
| else: |
| self.incrementals = copy.deepcopy(config_incrementals) |
| |
| self.module_priority = ["user","default"] |
| self.modules = {} |
| self.modules["user"] = getconfig(MODULES_FILE_PATH) |
| if self.modules["user"] == None: |
| self.modules["user"] = {} |
| self.modules["default"] = { |
| "portdbapi.metadbmodule": "portage_db_metadata.database", |
| "portdbapi.auxdbmodule": "portage_db_flat.database", |
| "eclass_cache.dbmodule": "portage_db_cpickle.database", |
| } |
| |
| self.usemask=[] |
| self.configlist=[] |
| self.backupenv={} |
| # back up our incremental variables: |
| self.configdict={} |
| # configlist will contain: [ globals, defaults, conf, pkg, auto, backupenv (incrementals), origenv ] |
| |
| # The symlink might not exist or might not be a symlink. |
| try: |
| self.profiles=[abssymlink(self.profile_path)] |
| except SystemExit, e: |
| raise |
| except: |
| self.profiles=[self.profile_path] |
| |
| mypath = self.profiles[0] |
| while os.path.exists(mypath+"/parent"): |
| mypath = os.path.normpath(mypath+"///"+grabfile(mypath+"/parent")[0]) |
| if os.path.exists(mypath): |
| self.profiles.insert(0,mypath) |
| |
| if os.environ.has_key("PORTAGE_CALLER") and os.environ["PORTAGE_CALLER"] == "repoman": |
| pass |
| else: |
| # XXX: This should depend on ROOT? |
| if os.path.exists("/"+CUSTOM_PROFILE_PATH): |
| self.user_profile_dir = os.path.normpath("/"+"///"+CUSTOM_PROFILE_PATH) |
| self.profiles.append(self.user_profile_dir[:]) |
| |
| self.packages_list = grab_multiple("packages", self.profiles, grabfile_package) |
| self.packages = stack_lists(self.packages_list, incremental=1) |
| del self.packages_list |
| #self.packages = grab_stacked("packages", self.profiles, grabfile, incremental_lines=1) |
| |
| # revmaskdict |
| self.prevmaskdict={} |
| for x in self.packages: |
| mycatpkg=dep_getkey(x) |
| if not self.prevmaskdict.has_key(mycatpkg): |
| self.prevmaskdict[mycatpkg]=[x] |
| else: |
| self.prevmaskdict[mycatpkg].append(x) |
| |
| # get profile-masked use flags -- INCREMENTAL Child over parent |
| usemask_lists = grab_multiple("use.mask", self.profiles, grabfile) |
| self.usemask = stack_lists(usemask_lists, incremental=True) |
| del usemask_lists |
| use_defs_lists = grab_multiple("use.defaults", self.profiles, grabdict) |
| self.use_defs = stack_dictlist(use_defs_lists, incremental=True) |
| del use_defs_lists |
| |
| try: |
| mygcfg_dlists = grab_multiple("make.globals", self.profiles+["/etc"], getconfig) |
| self.mygcfg = stack_dicts(mygcfg_dlists, incrementals=portage_const.INCREMENTALS, ignore_none=1) |
| |
| if self.mygcfg == None: |
| self.mygcfg = {} |
| except SystemExit, e: |
| raise |
| except Exception, e: |
| writemsg("!!! %s\n" % (e)) |
| writemsg("!!! Incorrect multiline literals can cause this. Do not use them.\n") |
| writemsg("!!! Errors in this file should be reported on bugs.gentoo.org.\n") |
| sys.exit(1) |
| self.configlist.append(self.mygcfg) |
| self.configdict["globals"]=self.configlist[-1] |
| |
| self.mygcfg = {} |
| if self.profiles: |
| try: |
| mygcfg_dlists = grab_multiple("make.defaults", self.profiles, getconfig) |
| self.mygcfg = stack_dicts(mygcfg_dlists, incrementals=portage_const.INCREMENTALS, ignore_none=1) |
| #self.mygcfg = grab_stacked("make.defaults", self.profiles, getconfig) |
| if self.mygcfg == None: |
| self.mygcfg = {} |
| except SystemExit, e: |
| raise |
| except Exception, e: |
| writemsg("!!! %s\n" % (e)) |
| writemsg("!!! 'rm -Rf /usr/portage/profiles; emerge sync' may fix this. If it does\n") |
| writemsg("!!! not then please report this to bugs.gentoo.org and, if possible, a dev\n") |
| writemsg("!!! on #gentoo (irc.freenode.org)\n") |
| sys.exit(1) |
| self.configlist.append(self.mygcfg) |
| self.configdict["defaults"]=self.configlist[-1] |
| |
| try: |
| # XXX: Should depend on root? |
| self.mygcfg=getconfig("/"+MAKE_CONF_FILE,allow_sourcing=True) |
| if self.mygcfg == None: |
| self.mygcfg = {} |
| except SystemExit, e: |
| raise |
| except Exception, e: |
| writemsg("!!! %s\n" % (e)) |
| writemsg("!!! Incorrect multiline literals can cause this. Do not use them.\n") |
| sys.exit(1) |
| |
| |
| self.configlist.append(self.mygcfg) |
| self.configdict["conf"]=self.configlist[-1] |
| |
| self.configlist.append({}) |
| self.configdict["pkg"]=self.configlist[-1] |
| |
| #auto-use: |
| self.configlist.append({}) |
| self.configdict["auto"]=self.configlist[-1] |
| |
| #backup-env (for recording our calculated incremental variables:) |
| self.backupenv = os.environ.copy() |
| self.configlist.append(self.backupenv) # XXX Why though? |
| self.configdict["backupenv"]=self.configlist[-1] |
| |
| self.configlist.append(os.environ.copy()) |
| self.configdict["env"]=self.configlist[-1] |
| |
| |
| # make lookuplist for loading package.* |
| self.lookuplist=self.configlist[:] |
| self.lookuplist.reverse() |
| |
| archlist = grabfile(self["PORTDIR"]+"/profiles/arch.list") |
| self.configdict["conf"]["PORTAGE_ARCHLIST"] = string.join(archlist) |
| |
| if os.environ.get("PORTAGE_CALLER","") == "repoman": |
| # repoman shouldn't use local settings. |
| locations = [self["PORTDIR"] + "/profiles"] |
| self.pusedict = {} |
| self.pkeywordsdict = {} |
| self.punmaskdict = {} |
| else: |
| locations = [self["PORTDIR"] + "/profiles", USER_CONFIG_PATH] |
| for ov in self["PORTDIR_OVERLAY"].split(): |
| ov = os.path.normpath(ov) |
| if os.path.isdir(ov+"/profiles"): |
| locations.append(ov+"/profiles") |
| |
| pusedict=grabdict_package(USER_CONFIG_PATH+"/package.use") |
| self.pusedict = {} |
| for key in pusedict.keys(): |
| cp = dep_getkey(key) |
| if not self.pusedict.has_key(cp): |
| self.pusedict[cp] = {} |
| self.pusedict[cp][key] = pusedict[key] |
| |
| #package.keywords |
| pkgdict=grabdict_package(USER_CONFIG_PATH+"/package.keywords") |
| self.pkeywordsdict = {} |
| for key in pkgdict.keys(): |
| # default to ~arch if no specific keyword is given |
| if not pkgdict[key]: |
| mykeywordlist = [] |
| if self.configdict["defaults"] and self.configdict["defaults"].has_key("ACCEPT_KEYWORDS"): |
| groups = self.configdict["defaults"]["ACCEPT_KEYWORDS"].split() |
| else: |
| groups = [] |
| for keyword in groups: |
| if not keyword[0] in "~-": |
| mykeywordlist.append("~"+keyword) |
| pkgdict[key] = mykeywordlist |
| cp = dep_getkey(key) |
| if not self.pkeywordsdict.has_key(cp): |
| self.pkeywordsdict[cp] = {} |
| self.pkeywordsdict[cp][key] = pkgdict[key] |
| |
| #package.unmask |
| pkgunmasklines = grabfile_package(USER_CONFIG_PATH+"/package.unmask") |
| self.punmaskdict = {} |
| for x in pkgunmasklines: |
| mycatpkg=dep_getkey(x) |
| if self.punmaskdict.has_key(mycatpkg): |
| self.punmaskdict[mycatpkg].append(x) |
| else: |
| self.punmaskdict[mycatpkg]=[x] |
| |
| #getting categories from an external file now |
| categories = grab_multiple("categories", locations, grabfile) |
| self.categories = stack_lists(categories, incremental=1) |
| del categories |
| |
| # get virtuals -- needs categories |
| self.loadVirtuals('/') |
| |
| #package.mask |
| pkgmasklines = grab_multiple("package.mask", self.profiles + locations, grabfile_package) |
| pkgmasklines = stack_lists(pkgmasklines, incremental=1) |
| |
| self.pmaskdict = {} |
| for x in pkgmasklines: |
| mycatpkg=dep_getkey(x) |
| if self.pmaskdict.has_key(mycatpkg): |
| self.pmaskdict[mycatpkg].append(x) |
| else: |
| self.pmaskdict[mycatpkg]=[x] |
| |
| pkgprovidedlines = grab_multiple("package.provided", self.profiles, grabfile) |
| pkgprovidedlines = stack_lists(pkgprovidedlines, incremental=1) |
| for x in range(len(pkgprovidedlines)-1, -1, -1): |
| cpvr = catpkgsplit(pkgprovidedlines[x]) |
| if not cpvr or cpvr[0] == "null": |
| writemsg("Invalid package name in package.provided: "+pkgprovidedlines[x]+"\n") |
| del pkgprovidedlines[x] |
| |
| self.pprovideddict = {} |
| for x in pkgprovidedlines: |
| cpv=catpkgsplit(x) |
| if not x: |
| continue |
| mycatpkg=dep_getkey(x) |
| if self.pprovideddict.has_key(mycatpkg): |
| self.pprovideddict[mycatpkg].append(x) |
| else: |
| self.pprovideddict[mycatpkg]=[x] |
| |
| self.lookuplist=self.configlist[:] |
| self.lookuplist.reverse() |
| |
| useorder=self["USE_ORDER"] |
| if not useorder: |
| # reasonable defaults; this is important as without USE_ORDER, |
| # USE will always be "" (nothing set)! |
| useorder="env:pkg:conf:auto:defaults" |
| useordersplit=useorder.split(":") |
| |
| self.uvlist=[] |
| for x in useordersplit: |
| if self.configdict.has_key(x): |
| if "PKGUSE" in self.configdict[x].keys(): |
| del self.configdict[x]["PKGUSE"] # Delete PkgUse, Not legal to set. |
| #prepend db to list to get correct order |
| self.uvlist[0:0]=[self.configdict[x]] |
| |
| self.configdict["env"]["PORTAGE_GID"]=str(portage_gid) |
| self.backupenv["PORTAGE_GID"]=str(portage_gid) |
| |
| if self.has_key("PORT_LOGDIR") and not self["PORT_LOGDIR"]: |
| # port_logdir is defined, but empty. this causes a traceback in doebuild. |
| writemsg(yellow("!!!")+" PORT_LOGDIR was defined, but set to nothing.\n") |
| writemsg(yellow("!!!")+" Disabling it. Please set it to a non null value.\n") |
| del self["PORT_LOGDIR"] |
| |
| if self["PORTAGE_CACHEDIR"]: |
| # XXX: Deprecated -- April 15 -- NJ |
| writemsg(yellow(">>> PORTAGE_CACHEDIR has been deprecated!")+"\n") |
| writemsg(">>> Please use PORTAGE_DEPCACHEDIR instead.\n") |
| self.depcachedir = self["PORTAGE_CACHEDIR"] |
| del self["PORTAGE_CACHEDIR"] |
| |
| if self["PORTAGE_DEPCACHEDIR"]: |
| #the auxcache is the only /var/cache/edb/ entry that stays at / even when "root" changes. |
| # XXX: Could move with a CHROOT functionality addition. |
| self.depcachedir = self["PORTAGE_DEPCACHEDIR"] |
| del self["PORTAGE_DEPCACHEDIR"] |
| |
| overlays = string.split(self["PORTDIR_OVERLAY"]) |
| if overlays: |
| new_ov=[] |
| for ov in overlays: |
| ov=os.path.normpath(ov) |
| if os.path.isdir(ov): |
| new_ov.append(ov) |
| else: |
| writemsg(red("!!! Invalid PORTDIR_OVERLAY (not a dir): "+ov+"\n")) |
| self["PORTDIR_OVERLAY"] = string.join(new_ov) |
| self.backup_changes("PORTDIR_OVERLAY") |
| |
| self.regenerate() |
| |
| self.features = portage_util.unique_array(self["FEATURES"].split()) |
| |
| #XXX: Should this be temporary? Is it possible at all to have a default? |
| if "gpg" in self.features: |
| if not os.path.exists(self["PORTAGE_GPG_DIR"]) or not os.path.isdir(self["PORTAGE_GPG_DIR"]): |
| writemsg("PORTAGE_GPG_DIR is invalid. Removing gpg from FEATURES.\n") |
| self.features.remove("gpg") |
| |
| if "maketest" in self.features and "test" not in self.features: |
| self.features.append("test") |
| |
| if not portage_exec.sandbox_capable and ("sandbox" in self.features or "usersandbox" in self.features): |
| writemsg(red("!!! Problem with sandbox binary. Disabling...\n\n")) |
| if "sandbox" in self.features: |
| self.features.remove("sandbox") |
| if "usersandbox" in self.features: |
| self.features.remove("usersandbox") |
| |
| self.features.sort() |
| self["FEATURES"] = " ".join(["-*"]+self.features) |
| self.backup_changes("FEATURES") |
| |
| if not len(self["CBUILD"]) and len(self["CHOST"]): |
| self["CBUILD"] = self["CHOST"] |
| self.backup_changes("CBUILD") |
| |
| if mycpv: |
| self.setcpv(mycpv) |
| |
| def loadVirtuals(self,root): |
| self.virtuals = self.getvirtuals(root) |
| |
| def load_best_module(self,property_string): |
| best_mod = best_from_dict(property_string,self.modules,self.module_priority) |
| return load_mod(best_mod) |
| |
| def lock(self): |
| self.locked = 1 |
| |
| def unlock(self): |
| self.locked = 0 |
| |
| def modifying(self): |
| if self.locked: |
| raise Exception, "Configuration is locked." |
| |
| def backup_changes(self,key=None): |
| if key and self.configdict["env"].has_key(key): |
| self.backupenv[key] = copy.deepcopy(self.configdict["env"][key]) |
| else: |
| raise KeyError, "No such key defined in environment: %s" % key |
| |
| def reset(self,keeping_pkg=0,use_cache=1): |
| "reset environment to original settings" |
| for x in self.configlist[-1].keys(): |
| if x not in self.backupenv.keys(): |
| del self.configlist[-1][x] |
| |
| self.configdict["env"].update(self.backupenv) |
| |
| self.modifiedkeys = [] |
| if not keeping_pkg: |
| self.puse = "" |
| self.configdict["pkg"].clear() |
| self.regenerate(use_cache=use_cache) |
| |
| def load_infodir(self,infodir): |
| if self.configdict.has_key("pkg"): |
| for x in self.configdict["pkg"].keys(): |
| del self.configdict["pkg"][x] |
| else: |
| writemsg("No pkg setup for settings instance?\n") |
| sys.exit(17) |
| |
| if os.path.exists(infodir): |
| if os.path.exists(infodir+"/environment"): |
| self.configdict["pkg"]["PORT_ENV_FILE"] = infodir+"/environment" |
| |
| myre = re.compile('^[A-Z]+$') |
| for filename in listdir(infodir,filesonly=1,EmptyOnError=1): |
| if myre.match(filename): |
| try: |
| mydata = string.strip(open(infodir+"/"+filename).read()) |
| if len(mydata)<2048: |
| if filename == "USE": |
| self.configdict["pkg"][filename] = "-* "+mydata |
| else: |
| self.configdict["pkg"][filename] = mydata |
| except SystemExit, e: |
| raise |
| except: |
| writemsg("!!! Unable to read file: %s\n" % infodir+"/"+filename) |
| pass |
| return 1 |
| return 0 |
| |
| def setcpv(self,mycpv,use_cache=1): |
| self.modifying() |
| self.mycpv = mycpv |
| cp = dep_getkey(mycpv) |
| newpuse = "" |
| if self.pusedict.has_key(cp): |
| self.pusekey = best_match_to_list(self.mycpv, self.pusedict[cp].keys()) |
| if self.pusekey: |
| newpuse = string.join(self.pusedict[cp][self.pusekey]) |
| if newpuse == self.puse: |
| return |
| self.puse = newpuse |
| self.configdict["pkg"]["PKGUSE"] = self.puse[:] # For saving to PUSE file |
| self.configdict["pkg"]["USE"] = self.puse[:] # this gets appended to USE |
| self.reset(keeping_pkg=1,use_cache=use_cache) |
| |
| def setinst(self,mycpv,mydbapi): |
| # Grab the virtuals this package provides and add them into the tree virtuals. |
| provides = mydbapi.aux_get(mycpv, ["PROVIDE"])[0] |
| if isinstance(mydbapi, portdbapi): |
| myuse = self["USE"] |
| else: |
| myuse = mydbapi.aux_get(mycpv, ["USE"])[0] |
| virts = flatten(portage_dep.use_reduce(portage_dep.paren_reduce(provides), uselist=myuse.split())) |
| |
| cp = dep_getkey(mycpv) |
| for virt in virts: |
| virt = dep_getkey(virt) |
| if not self.treeVirtuals.has_key(virt): |
| self.treeVirtuals[virt] = [] |
| # XXX: Is this bad? -- It's a permanent modification |
| self.treeVirtuals[virt] = portage_util.unique_array(self.treeVirtuals[virt]+[cp]) |
| |
| self.virtuals = self.__getvirtuals_compile() |
| |
| |
| def regenerate(self,useonly=0,use_cache=1): |
| global usesplit,profiledir |
| |
| if self.already_in_regenerate: |
| # XXX: THIS REALLY NEEDS TO GET FIXED. autouse() loops. |
| writemsg("!!! Looping in regenerate.\n",1) |
| return |
| else: |
| self.already_in_regenerate = 1 |
| |
| if useonly: |
| myincrementals=["USE"] |
| else: |
| myincrementals=portage_const.INCREMENTALS |
| for mykey in myincrementals: |
| if mykey=="USE": |
| mydbs=self.uvlist |
| # XXX Global usage of db... Needs to go away somehow. |
| if db.has_key(root) and db[root].has_key("vartree"): |
| self.configdict["auto"]["USE"]=autouse(db[root]["vartree"],use_cache=use_cache) |
| else: |
| self.configdict["auto"]["USE"]="" |
| else: |
| mydbs=self.configlist[:-1] |
| |
| myflags=[] |
| for curdb in mydbs: |
| if not curdb.has_key(mykey): |
| continue |
| #variables are already expanded |
| mysplit=curdb[mykey].split() |
| |
| for x in mysplit: |
| if x=="-*": |
| # "-*" is a special "minus" var that means "unset all settings". |
| # so USE="-* gnome" will have *just* gnome enabled. |
| myflags=[] |
| continue |
| |
| if x[0]=="+": |
| # Not legal. People assume too much. Complain. |
| writemsg(red("USE flags should not start with a '+': %s\n" % x)) |
| x=x[1:] |
| |
| if (x[0]=="-"): |
| if (x[1:] in myflags): |
| # Unset/Remove it. |
| del myflags[myflags.index(x[1:])] |
| continue |
| |
| # We got here, so add it now. |
| if x not in myflags: |
| myflags.append(x) |
| |
| myflags.sort() |
| #store setting in last element of configlist, the original environment: |
| self.configlist[-1][mykey]=string.join(myflags," ") |
| del myflags |
| |
| #cache split-up USE var in a global |
| usesplit=[] |
| |
| for x in string.split(self.configlist[-1]["USE"]): |
| if x not in self.usemask: |
| usesplit.append(x) |
| |
| if self.has_key("USE_EXPAND"): |
| for var in string.split(self["USE_EXPAND"]): |
| if self.has_key(var): |
| for x in string.split(self[var]): |
| mystr = string.lower(var)+"_"+x |
| if mystr not in usesplit: |
| usesplit.append(mystr) |
| |
| # Pre-Pend ARCH variable to USE settings so '-*' in env doesn't kill arch. |
| if self.configdict["defaults"].has_key("ARCH"): |
| if self.configdict["defaults"]["ARCH"]: |
| if self.configdict["defaults"]["ARCH"] not in usesplit: |
| usesplit.insert(0,self.configdict["defaults"]["ARCH"]) |
| |
| self.configlist[-1]["USE"]=string.join(usesplit," ") |
| |
| self.already_in_regenerate = 0 |
| |
| def getvirtuals(self, myroot): |
| myvirts = {} |
| |
| # This breaks catalyst/portage when setting to a fresh/empty root. |
| # Virtuals cannot be calculated because there is nothing to work |
| # from. So the only ROOT prefixed dir should be local configs. |
| #myvirtdirs = prefix_array(self.profiles,myroot+"/") |
| myvirtdirs = copy.deepcopy(self.profiles) |
| while self.user_profile_dir in myvirtdirs: |
| myvirtdirs.remove(self.user_profile_dir) |
| |
| |
| # Rules |
| # R1: Collapse profile virtuals |
| # R2: Extract user-negatives. |
| # R3: Collapse user-virtuals. |
| # R4: Apply user negatives to all except user settings. |
| |
| # Order of preference: |
| # 1. user-declared that are installed |
| # 3. installed and in profile |
| # 4. installed |
| # 2. user-declared set |
| # 5. profile |
| |
| self.dirVirtuals = grab_multiple("virtuals", myvirtdirs, grabdict) |
| self.dirVirtuals.reverse() |
| |
| if self.user_profile_dir and os.path.exists(self.user_profile_dir+"/virtuals"): |
| self.userVirtuals = grabdict(self.user_profile_dir+"/virtuals") |
| |
| # Store all the negatives for later. |
| for x in self.userVirtuals.keys(): |
| self.negVirtuals[x] = [] |
| for y in self.userVirtuals[x]: |
| if y[0] == '-': |
| self.negVirtuals[x].append(y[:]) |
| |
| # Collapse the user virtuals so that we don't deal with negatives. |
| self.userVirtuals = stack_dictlist([self.userVirtuals],incremental=1) |
| |
| # Collapse all the profile virtuals including user negations. |
| self.dirVirtuals = stack_dictlist([self.negVirtuals]+self.dirVirtuals,incremental=1) |
| |
| # Repoman does not use user or tree virtuals. |
| if os.environ.get("PORTAGE_CALLER","") != "repoman": |
| # XXX: vartree does not use virtuals, does user set matter? |
| temp_vartree = vartree(myroot,self.dirVirtuals,categories=self.categories) |
| # Reduce the provides into a list by CP. |
| self.treeVirtuals = map_dictlist_vals(getCPFromCPV,temp_vartree.get_all_provides()) |
| |
| return self.__getvirtuals_compile() |
| |
| def __getvirtuals_compile(self): |
| """Actually generate the virtuals we have collected. |
| The results are reversed so the list order is left to right. |
| Given data is [Best,Better,Good] sets of [Good, Better, Best]""" |
| |
| # Virtuals by profile+tree preferences. |
| ptVirtuals = {} |
| # Virtuals by user+tree preferences. |
| utVirtuals = {} |
| |
| # If a user virtual is already installed, we preference it. |
| for x in self.userVirtuals.keys(): |
| utVirtuals[x] = [] |
| if self.treeVirtuals.has_key(x): |
| for y in self.userVirtuals[x]: |
| if y in self.treeVirtuals[x]: |
| utVirtuals[x].append(y) |
| #print "F:",utVirtuals |
| #utVirtuals[x].reverse() |
| #print "R:",utVirtuals |
| |
| # If a profile virtual is already installed, we preference it. |
| for x in self.dirVirtuals.keys(): |
| ptVirtuals[x] = [] |
| if self.treeVirtuals.has_key(x): |
| for y in self.dirVirtuals[x]: |
| if y in self.treeVirtuals[x]: |
| ptVirtuals[x].append(y) |
| |
| # UserInstalled, ProfileInstalled, Installed, User, Profile |
| biglist = [utVirtuals, ptVirtuals, self.treeVirtuals, |
| self.userVirtuals, self.dirVirtuals] |
| |
| # We reverse each dictlist so that the order matches everything |
| # else in portage. [-*, a, b] [b, c, d] ==> [b, a] |
| for dictlist in biglist: |
| for key in dictlist: |
| dictlist[key].reverse() |
| |
| # User settings and profile settings take precedence over tree. |
| val = stack_dictlist(biglist,incremental=1) |
| |
| return val |
| |
| def __delitem__(self,mykey): |
| for x in self.lookuplist: |
| if x != None: |
| if mykey in x: |
| del x[mykey] |
| |
| def __getitem__(self,mykey): |
| match = '' |
| for x in self.lookuplist: |
| if x == None: |
| writemsg("!!! lookuplist is null.\n") |
| elif x.has_key(mykey): |
| match = x[mykey] |
| break |
| |
| if 0 and match and mykey in ["PORTAGE_BINHOST"]: |
| # These require HTTP Encoding |
| try: |
| import urllib |
| if urllib.unquote(match) != match: |
| writemsg("Note: %s already contains escape codes." % (mykey)) |
| else: |
| match = urllib.quote(match) |
| except SystemExit, e: |
| raise |
| except: |
| writemsg("Failed to fix %s using urllib, attempting to continue.\n" % (mykey)) |
| pass |
| |
| elif mykey == "CONFIG_PROTECT_MASK": |
| match += " /etc/env.d" |
| |
| return match |
| |
| def has_key(self,mykey): |
| for x in self.lookuplist: |
| if x.has_key(mykey): |
| return 1 |
| return 0 |
| |
| def keys(self): |
| mykeys=[] |
| for x in self.lookuplist: |
| for y in x.keys(): |
| if y not in mykeys: |
| mykeys.append(y) |
| return mykeys |
| |
| def __setitem__(self,mykey,myvalue): |
| "set a value; will be thrown away at reset() time" |
| if type(myvalue) != types.StringType: |
| raise ValueError("Invalid type being used as a value: '%s': '%s'" % (str(mykey),str(myvalue))) |
| self.modifying() |
| self.modifiedkeys += [mykey] |
| self.configdict["env"][mykey]=myvalue |
| |
| def environ(self): |
| "return our locally-maintained environment" |
| mydict={} |
| for x in self.keys(): |
| mydict[x]=self[x] |
| if not mydict.has_key("HOME") and mydict.has_key("BUILD_PREFIX"): |
| writemsg("*** HOME not set. Setting to "+mydict["BUILD_PREFIX"]+"\n") |
| mydict["HOME"]=mydict["BUILD_PREFIX"][:] |
| |
| return mydict |
| |
| |
| # XXX This would be to replace getstatusoutput completely. |
| # XXX Issue: cannot block execution. Deadlock condition. |
| def spawn(mystring,mysettings,debug=0,free=0,droppriv=0,fd_pipes=None,**keywords): |
| """spawn a subprocess with optional sandbox protection, |
| depending on whether sandbox is enabled. The "free" argument, |
| when set to 1, will disable sandboxing. This allows us to |
| spawn processes that are supposed to modify files outside of the |
| sandbox. We can't use os.system anymore because it messes up |
| signal handling. Using spawn allows our Portage signal handler |
| to work.""" |
| |
| if type(mysettings) == types.DictType: |
| env=mysettings |
| keywords["opt_name"]="[ %s ]" % "portage" |
| else: |
| check_config_instance(mysettings) |
| env=mysettings.environ() |
| keywords["opt_name"]="[%s]" % mysettings["PF"] |
| |
| # XXX: Negative RESTRICT word |
| droppriv=(droppriv and ("userpriv" in features) and not \ |
| (("nouserpriv" in string.split(mysettings["RESTRICT"])) or \ |
| ("userpriv" in string.split(mysettings["RESTRICT"])))) |
| |
| if droppriv and not uid and portage_gid and portage_uid: |
| keywords.update({"uid":portage_uid,"gid":portage_gid,"groups":[portage_gid],"umask":002}) |
| |
| if not free: |
| free=((droppriv and "usersandbox" not in features) or \ |
| (not droppriv and "sandbox" not in features and "usersandbox" not in features)) |
| |
| if not free: |
| keywords["opt_name"] += " sandbox" |
| return portage_exec.spawn_sandbox(mystring,env=env,**keywords) |
| else: |
| keywords["opt_name"] += " bash" |
| return portage_exec.spawn_bash(mystring,env=env,**keywords) |
| |
| |
| |
| def fetch(myuris, mysettings, listonly=0, fetchonly=0, locks_in_subdir=".locks",use_locks=1, try_mirrors=1): |
| "fetch files. Will use digest file if available." |
| |
| # 'nomirror' is bad/negative logic. You Restrict mirroring, not no-mirroring. |
| if ("mirror" in mysettings["RESTRICT"].split()) or \ |
| ("nomirror" in mysettings["RESTRICT"].split()): |
| if ("mirror" in features) and ("lmirror" not in features): |
| # lmirror should allow you to bypass mirror restrictions. |
| # XXX: This is not a good thing, and is temporary at best. |
| print ">>> \"mirror\" mode desired and \"mirror\" restriction found; skipping fetch." |
| return 1 |
| |
| global thirdpartymirrors |
| |
| check_config_instance(mysettings) |
| |
| custommirrors=grabdict(CUSTOM_MIRRORS_FILE) |
| |
| mymirrors=[] |
| |
| if listonly or ("distlocks" not in features): |
| use_locks = 0 |
| |
| fetch_to_ro = 0 |
| if "skiprocheck" in features: |
| fetch_to_ro = 1 |
| |
| if not os.access(mysettings["DISTDIR"],os.W_OK) and fetch_to_ro: |
| if use_locks: |
| writemsg(red("!!! You are fetching to a read-only filesystem, you should turn locking off")); |
| writemsg("!!! This can be done by adding -distlocks to FEATURES in /etc/make.conf"); |
| # use_locks = 0 |
| |
| # local mirrors are always added |
| if custommirrors.has_key("local"): |
| mymirrors += custommirrors["local"] |
| |
| if ("nomirror" in mysettings["RESTRICT"].split()) or \ |
| ("mirror" in mysettings["RESTRICT"].split()): |
| # We don't add any mirrors. |
| pass |
| else: |
| if try_mirrors: |
| for x in mysettings["GENTOO_MIRRORS"].split(): |
| if x: |
| if x[-1] == '/': |
| mymirrors += [x[:-1]] |
| else: |
| mymirrors += [x] |
| |
| mydigests = {} |
| digestfn = mysettings["FILESDIR"]+"/digest-"+mysettings["PF"] |
| if os.path.exists(digestfn): |
| mydigests = digestParseFile(digestfn) |
| |
| fsmirrors = [] |
| for x in range(len(mymirrors)-1,-1,-1): |
| if mymirrors[x] and mymirrors[x][0]=='/': |
| fsmirrors += [mymirrors[x]] |
| del mymirrors[x] |
| |
| for myuri in myuris: |
| myfile=os.path.basename(myuri) |
| try: |
| destdir = mysettings["DISTDIR"]+"/" |
| if not os.path.exists(destdir+myfile): |
| for mydir in fsmirrors: |
| if os.path.exists(mydir+"/"+myfile): |
| writemsg(_("Local mirror has file: %(file)s\n" % {"file":myfile})) |
| shutil.copyfile(mydir+"/"+myfile,destdir+"/"+myfile) |
| break |
| except (OSError,IOError),e: |
| # file does not exist |
| writemsg(_("!!! %(file)s not found in %(dir)s\n") % {"file":myfile, "dir":mysettings["DISTDIR"]}) |
| gotit=0 |
| |
| if "fetch" in mysettings["RESTRICT"].split(): |
| # fetch is restricted. Ensure all files have already been downloaded; otherwise, |
| # print message and exit. |
| gotit=1 |
| for myuri in myuris: |
| myfile=os.path.basename(myuri) |
| try: |
| mystat=os.stat(mysettings["DISTDIR"]+"/"+myfile) |
| except (OSError,IOError),e: |
| # file does not exist |
| writemsg(_("!!! %(file)s not found in %(dir)s\n") % {"file":myfile, "dir":mysettings["DISTDIR"]}) |
| gotit=0 |
| if not gotit: |
| print |
| print "!!!",mysettings["CATEGORY"]+"/"+mysettings["PF"],"has fetch restriction turned on." |
| print "!!! This probably means that this ebuild's files must be downloaded" |
| print "!!! manually. See the comments in the ebuild for more information." |
| print |
| spawn(EBUILD_SH_BINARY+" nofetch",mysettings) |
| return 0 |
| return 1 |
| locations=mymirrors[:] |
| filedict={} |
| primaryuri_indexes={} |
| for myuri in myuris: |
| myfile=os.path.basename(myuri) |
| if not filedict.has_key(myfile): |
| filedict[myfile]=[] |
| for y in range(0,len(locations)): |
| filedict[myfile].append(locations[y]+"/distfiles/"+myfile) |
| if myuri[:9]=="mirror://": |
| eidx = myuri.find("/", 9) |
| if eidx != -1: |
| mirrorname = myuri[9:eidx] |
| |
| # Try user-defined mirrors first |
| if custommirrors.has_key(mirrorname): |
| for cmirr in custommirrors[mirrorname]: |
| filedict[myfile].append(cmirr+"/"+myuri[eidx+1:]) |
| # remove the mirrors we tried from the list of official mirrors |
| if cmirr.strip() in thirdpartymirrors[mirrorname]: |
| thirdpartymirrors[mirrorname].remove(cmirr) |
| # now try the official mirrors |
| if thirdpartymirrors.has_key(mirrorname): |
| try: |
| shuffle(thirdpartymirrors[mirrorname]) |
| except SystemExit, e: |
| raise |
| except: |
| writemsg(red("!!! YOU HAVE A BROKEN PYTHON/GLIBC.\n")) |
| writemsg( "!!! You are most likely on a pentium4 box and have specified -march=pentium4\n") |
| writemsg( "!!! or -fpmath=sse2. GCC was generating invalid sse2 instructions in versions\n") |
| writemsg( "!!! prior to 3.2.3. Please merge the latest gcc or rebuid python with either\n") |
| writemsg( "!!! -march=pentium3 or set -mno-sse2 in your cflags.\n\n\n") |
| time.sleep(10) |
| |
| for locmirr in thirdpartymirrors[mirrorname]: |
| filedict[myfile].append(locmirr+"/"+myuri[eidx+1:]) |
| |
| if not filedict[myfile]: |
| writemsg("No known mirror by the name: %s\n" % (mirrorname)) |
| else: |
| writemsg("Invalid mirror definition in SRC_URI:\n") |
| writemsg(" %s\n" % (myuri)) |
| else: |
| if "primaryuri" in mysettings["RESTRICT"].split(): |
| # Use the source site first. |
| if primaryuri_indexes.has_key(myfile): |
| primaryuri_indexes[myfile] += 1 |
| else: |
| primaryuri_indexes[myfile] = 0 |
| filedict[myfile].insert(primaryuri_indexes[myfile], myuri) |
| else: |
| filedict[myfile].append(myuri) |
| |
| missingSourceHost = False |
| for myfile in filedict.keys(): # Gives a list, not just the first one |
| if not filedict[myfile]: |
| writemsg("Warning: No mirrors available for file '%s'\n" % (myfile)) |
| missingSourceHost = True |
| if missingSourceHost: |
| return 0 |
| del missingSourceHost |
| |
| can_fetch=True |
| if not os.access(mysettings["DISTDIR"]+"/",os.W_OK): |
| if not fetch_to_ro: |
| print "!!! No write access to %s" % mysettings["DISTDIR"]+"/" |
| can_fetch=False |
| else: |
| mystat=os.stat(mysettings["DISTDIR"]+"/") |
| if mystat.st_gid != portage_gid: |
| try: |
| os.chown(mysettings["DISTDIR"],-1,portage_gid) |
| except OSError, oe: |
| if oe.errno == 1: |
| print red("!!!")+" Unable to chgrp of %s to portage, continuing\n" % mysettings["DISTDIR"] |
| else: |
| raise oe |
| |
| # writable by portage_gid? This is specific to root, adjust perms if needed automatically. |
| if not stat.S_IMODE(mystat.st_mode) & 020: |
| try: |
| os.chmod(mysettings["DISTDIR"],stat.S_IMODE(mystat.st_mode) | 020) |
| except OSError, oe: |
| if oe.errno == 1: |
| print red("!!!")+" Unable to chmod %s to perms 0755. Non-root users will experience issues.\n" % mysettings["DISTDIR"] |
| else: |
| raise oe |
| |
| if use_locks and locks_in_subdir: |
| if os.path.exists(mysettings["DISTDIR"]+"/"+locks_in_subdir): |
| if not os.access(mysettings["DISTDIR"]+"/"+locks_in_subdir,os.W_OK): |
| writemsg("!!! No write access to write to %s. Aborting.\n" % mysettings["DISTDIR"]+"/"+locks_in_subdir) |
| return 0 |
| else: |
| old_umask=os.umask(0002) |
| os.mkdir(mysettings["DISTDIR"]+"/"+locks_in_subdir,0775) |
| if os.stat(mysettings["DISTDIR"]+"/"+locks_in_subdir).st_gid != portage_gid: |
| try: |
| os.chown(mysettings["DISTDIR"]+"/"+locks_in_subdir,-1,portage_gid) |
| except SystemExit, e: |
| raise |
| except: |
| pass |
| os.umask(old_umask) |
| |
| |
| for myfile in filedict.keys(): |
| fetched=0 |
| file_lock = None |
| if listonly: |
| writemsg("\n") |
| else: |
| if use_locks and can_fetch: |
| if locks_in_subdir: |
| file_lock = portage_locks.lockfile(mysettings["DISTDIR"]+"/"+locks_in_subdir+"/"+myfile,wantnewlockfile=1) |
| else: |
| file_lock = portage_locks.lockfile(mysettings["DISTDIR"]+"/"+myfile,wantnewlockfile=1) |
| try: |
| for loc in filedict[myfile]: |
| if listonly: |
| writemsg(loc+" ") |
| continue |
| # allow different fetchcommands per protocol |
| protocol = loc[0:loc.find("://")] |
| if mysettings.has_key("FETCHCOMMAND_"+protocol.upper()): |
| fetchcommand=mysettings["FETCHCOMMAND_"+protocol.upper()] |
| else: |
| fetchcommand=mysettings["FETCHCOMMAND"] |
| if mysettings.has_key("RESUMECOMMAND_"+protocol.upper()): |
| resumecommand=mysettings["RESUMECOMMAND_"+protocol.upper()] |
| else: |
| resumecommand=mysettings["RESUMECOMMAND"] |
| |
| fetchcommand=string.replace(fetchcommand,"${DISTDIR}",mysettings["DISTDIR"]) |
| resumecommand=string.replace(resumecommand,"${DISTDIR}",mysettings["DISTDIR"]) |
| |
| try: |
| mystat=os.stat(mysettings["DISTDIR"]+"/"+myfile) |
| if mydigests.has_key(myfile): |
| #if we have the digest file, we know the final size and can resume the download. |
| if mystat[stat.ST_SIZE]<mydigests[myfile]["size"]: |
| fetched=1 |
| else: |
| #we already have it downloaded, skip. |
| #if our file is bigger than the recorded size, digestcheck should catch it. |
| if not fetchonly: |
| fetched=2 |
| else: |
| # Check md5sum's at each fetch for fetchonly. |
| verified_ok,reason = portage_checksum.verify_all(mysettings["DISTDIR"]+"/"+myfile, mydigests[myfile]) |
| if not verified_ok: |
| writemsg("!!! Previously fetched file: "+str(myfile)+"\n!!! Reason: "+reason+"\nRefetching...\n\n") |
| os.unlink(mysettings["DISTDIR"]+"/"+myfile) |
| fetched=0 |
| else: |
| for x_key in mydigests[myfile].keys(): |
| writemsg(">>> Previously fetched file: "+str(myfile)+" "+x_key+" ;-)\n") |
| fetched=2 |
| break #No need to keep looking for this file, we have it! |
| else: |
| #we don't have the digest file, but the file exists. Assume it is fully downloaded. |
| fetched=2 |
| except (OSError,IOError),e: |
| writemsg("An exception was caught(1)...\nFailing the download: %s.\n" % (str(e)),1) |
| fetched=0 |
| |
| if not can_fetch: |
| if fetched != 2: |
| if fetched == 0: |
| writemsg("!!! File %s isn't fetched but unable to get it.\n" % myfile) |
| else: |
| writemsg("!!! File %s isn't fully fetched, but unable to complete it\n" % myfile) |
| return 0 |
| else: |
| continue |
| |
| # check if we can actually write to the directory/existing file. |
| if fetched!=2 and os.path.exists(mysettings["DISTDIR"]+"/"+myfile) != \ |
| os.access(mysettings["DISTDIR"]+"/"+myfile, os.W_OK) and not fetch_to_ro: |
| writemsg(red("***")+" Lack write access to %s, failing fetch\n" % str(mysettings["DISTDIR"]+"/"+myfile)) |
| fetched=0 |
| break |
| elif fetched!=2: |
| #we either need to resume or start the download |
| #you can't use "continue" when you're inside a "try" block |
| if fetched==1: |
| #resume mode: |
| writemsg(">>> Resuming download...\n") |
| locfetch=resumecommand |
| else: |
| #normal mode: |
| locfetch=fetchcommand |
| writemsg(">>> Downloading "+str(loc)+"\n") |
| myfetch=string.replace(locfetch,"${URI}",loc) |
| myfetch=string.replace(myfetch,"${FILE}",myfile) |
| try: |
| if selinux_enabled: |
| con=selinux.getcontext() |
| con=string.replace(con,mysettings["PORTAGE_T"],mysettings["PORTAGE_FETCH_T"]) |
| selinux.setexec(con) |
| myret=spawn(myfetch,mysettings,free=1, droppriv=("userfetch" in mysettings.features)) |
| selinux.setexec(None) |
| else: |
| myret=spawn(myfetch,mysettings,free=1, droppriv=("userfetch" in mysettings.features)) |
| finally: |
| #if root, -always- set the perms. |
| if os.path.exists(mysettings["DISTDIR"]+"/"+myfile) and (fetched != 1 or os.getuid() == 0) \ |
| and os.access(mysettings["DISTDIR"]+"/",os.W_OK): |
| if os.stat(mysettings["DISTDIR"]+"/"+myfile).st_gid != portage_gid: |
| try: |
| os.chown(mysettings["DISTDIR"]+"/"+myfile,-1,portage_gid) |
| except SystemExit, e: |
| raise |
| except: |
| portage_util.writemsg("chown failed on distfile: " + str(myfile)) |
| os.chmod(mysettings["DISTDIR"]+"/"+myfile,0664) |
| |
| if mydigests!=None and mydigests.has_key(myfile): |
| try: |
| mystat=os.stat(mysettings["DISTDIR"]+"/"+myfile) |
| # no exception? file exists. let digestcheck() report |
| # an appropriately for size or md5 errors |
| if (mystat[stat.ST_SIZE]<mydigests[myfile]["size"]): |
| # Fetch failed... Try the next one... Kill 404 files though. |
| if (mystat[stat.ST_SIZE]<100000) and (len(myfile)>4) and not ((myfile[-5:]==".html") or (myfile[-4:]==".htm")): |
| html404=re.compile("<title>.*(not found|404).*</title>",re.I|re.M) |
| try: |
| if html404.search(open(mysettings["DISTDIR"]+"/"+myfile).read()): |
| try: |
| os.unlink(mysettings["DISTDIR"]+"/"+myfile) |
| writemsg(">>> Deleting invalid distfile. (Improper 404 redirect from server.)\n") |
| except SystemExit, e: |
| raise |
| except: |
| pass |
| except SystemExit, e: |
| raise |
| except: |
| pass |
| continue |
| if not fetchonly: |
| fetched=2 |
| break |
| else: |
| # File is the correct size--check the MD5 sum for the fetched |
| # file NOW, for those users who don't have a stable/continuous |
| # net connection. This way we have a chance to try to download |
| # from another mirror... |
| verified_ok,reason = portage_checksum.verify_all(mysettings["DISTDIR"]+"/"+myfile, mydigests[myfile]) |
| if not verified_ok: |
| writemsg("!!! Fetched file: "+str(myfile)+" VERIFY FAILED!\n!!! Reason: "+reason+"\nRemoving corrupt distfile...\n") |
| os.unlink(mysettings["DISTDIR"]+"/"+myfile) |
| fetched=0 |
| else: |
| for x_key in mydigests[myfile].keys(): |
| writemsg(">>> "+str(myfile)+" "+x_key+" ;-)\n") |
| fetched=2 |
| break |
| except (OSError,IOError),e: |
| writemsg("An exception was caught(2)...\nFailing the download: %s.\n" % (str(e)),1) |
| fetched=0 |
| else: |
| if not myret: |
| fetched=2 |
| break |
| elif mydigests!=None: |
| writemsg("No digest file available and download failed.\n\n") |
| finally: |
| if use_locks and file_lock: |
| portage_locks.unlockfile(file_lock) |
| |
| if listonly: |
| writemsg("\n") |
| if (fetched!=2) and not listonly: |
| writemsg("!!! Couldn't download "+str(myfile)+". Aborting.\n") |
| return 0 |
| return 1 |
| |
| |
| def digestCreate(myfiles,basedir,oldDigest={}): |
| """Takes a list of files and the directory they are in and returns the |
| dict of dict[filename][CHECKSUM_KEY] = hash |
| returns None on error.""" |
| mydigests={} |
| for x in myfiles: |
| print "<<<",x |
| myfile=os.path.normpath(basedir+"///"+x) |
| if os.path.exists(myfile): |
| if not os.access(myfile, os.R_OK): |
| print "!!! Given file does not appear to be readable. Does it exist?" |
| print "!!! File:",myfile |
| return None |
| mydigests[x] = portage_checksum.perform_all(myfile) |
| mysize = os.stat(myfile)[stat.ST_SIZE] |
| else: |
| if x in oldDigest: |
| # DeepCopy because we might not have a unique reference. |
| mydigests[x] = copy.deepcopy(oldDigest[x]) |
| mysize = copy.deepcopy(oldDigest[x]["size"]) |
| else: |
| print "!!! We have a source URI, but no file..." |
| print "!!! File:",myfile |
| return None |
| |
| if mydigests[x].has_key("size") and (mydigests[x]["size"] != mysize): |
| raise portage_exception.DigestException, "Size mismatch during checksums" |
| mydigests[x]["size"] = copy.deepcopy(mysize) |
| return mydigests |
| |
| def digestCreateLines(filelist, mydict): |
| mylines = [] |
| mydigests = copy.deepcopy(mydict) |
| for myarchive in filelist: |
| mysize = mydigests[myarchive]["size"] |
| if len(mydigests[myarchive]) == 0: |
| raise portage_exception.DigestException, "No generate digest for '%(file)s'" % {"file":myarchive} |
| for sumName in mydigests[myarchive].keys(): |
| if sumName not in portage_checksum.get_valid_checksum_keys(): |
| continue |
| mysum = mydigests[myarchive][sumName] |
| |
| myline = sumName[:] |
| myline += " "+mysum |
| myline += " "+myarchive |
| myline += " "+str(mysize) |
| if sumName != "MD5": |
| # XXXXXXXXXXXXXXXX This cannot be used! |
| # Older portage make very dumb assumptions about the formats. |
| # We need a lead-in period before we break everything. |
| continue |
| mylines.append(myline) |
| return mylines |
| |
| def digestgen(myarchives,mysettings,overwrite=1,manifestonly=0): |
| """generates digest file if missing. Assumes all files are available. If |
| overwrite=0, the digest will only be created if it doesn't already exist.""" |
| |
| # archive files |
| basedir=mysettings["DISTDIR"]+"/" |
| digestfn=mysettings["FILESDIR"]+"/digest-"+mysettings["PF"] |
| |
| # portage files -- p(ortagefiles)basedir |
| pbasedir=mysettings["O"]+"/" |
| manifestfn=pbasedir+"Manifest" |
| |
| if not manifestonly: |
| if not os.path.isdir(mysettings["FILESDIR"]): |
| os.makedirs(mysettings["FILESDIR"]) |
| mycvstree=cvstree.getentries(pbasedir, recursive=1) |
| |
| if ("cvs" in features) and os.path.exists(pbasedir+"/CVS"): |
| if not cvstree.isadded(mycvstree,"files"): |
| if "autoaddcvs" in features: |
| print ">>> Auto-adding files/ dir to CVS..." |
| spawn("cd "+pbasedir+"; cvs add files",mysettings,free=1) |
| else: |
| print "--- Warning: files/ is not added to cvs." |
| |
| if (not overwrite) and os.path.exists(digestfn): |
| return 1 |
| |
| print green(">>> Generating digest file...") |
| |
| # Track the old digest so we can assume checksums without requiring |
| # all files to be downloaded. 'Assuming' |
| myolddigest = {} |
| if os.path.exists(digestfn): |
| myolddigest = digestParseFile(digestfn) |
| |
| myarchives.sort() |
| mydigests=digestCreate(myarchives, basedir, oldDigest=myolddigest) |
| if mydigests==None: # There was a problem, exit with an errorcode. |
| return 0 |
| |
| try: |
| outfile=open(digestfn, "w+") |
| except SystemExit, e: |
| raise |
| except Exception, e: |
| print "!!! Filesystem error skipping generation. (Read-Only?)" |
| print "!!!",e |
| return 0 |
| for x in digestCreateLines(myarchives, mydigests): |
| outfile.write(x+"\n") |
| outfile.close() |
| try: |
| os.chown(digestfn,os.getuid(),portage_gid) |
| os.chmod(digestfn,0664) |
| except SystemExit, e: |
| raise |
| except Exception,e: |
| print e |
| |
| print green(">>> Generating manifest file...") |
| mypfiles=listdir(pbasedir,recursive=1,filesonly=1,ignorecvs=1,EmptyOnError=1) |
| mypfiles=cvstree.apply_cvsignore_filter(mypfiles) |
| mypfiles.sort() |
| for x in ["Manifest"]: |
| if x in mypfiles: |
| mypfiles.remove(x) |
| |
| mydigests=digestCreate(mypfiles, pbasedir) |
| if mydigests==None: # There was a problem, exit with an errorcode. |
| return 0 |
| |
| try: |
| outfile=open(manifestfn, "w+") |
| except SystemExit, e: |
| raise |
| except Exception, e: |
| print "!!! Filesystem error skipping generation. (Read-Only?)" |
| print "!!!",e |
| return 0 |
| for x in digestCreateLines(mypfiles, mydigests): |
| outfile.write(x+"\n") |
| outfile.close() |
| try: |
| os.chown(manifestfn,os.getuid(),portage_gid) |
| os.chmod(manifestfn,0664) |
| except SystemExit, e: |
| raise |
| except Exception,e: |
| print e |
| |
| if "cvs" in features and os.path.exists(pbasedir+"/CVS"): |
| mycvstree=cvstree.getentries(pbasedir, recursive=1) |
| myunaddedfiles="" |
| if not manifestonly and not cvstree.isadded(mycvstree,digestfn): |
| if digestfn[:len(pbasedir)]==pbasedir: |
| myunaddedfiles=digestfn[len(pbasedir):]+" " |
| else: |
| myunaddedfiles=digestfn+" " |
| if not cvstree.isadded(mycvstree,manifestfn[len(pbasedir):]): |
| if manifestfn[:len(pbasedir)]==pbasedir: |
| myunaddedfiles+=manifestfn[len(pbasedir):]+" " |
| else: |
| myunaddedfiles+=manifestfn |
| if myunaddedfiles: |
| if "autoaddcvs" in features: |
| print blue(">>> Auto-adding digest file(s) to CVS...") |
| spawn("cd "+pbasedir+"; cvs add "+myunaddedfiles,mysettings,free=1) |
| else: |
| print "--- Warning: digests are not yet added into CVS." |
| print darkgreen(">>> Computed message digests.") |
| print |
| return 1 |
| |
| |
| def digestParseFile(myfilename): |
| """(filename) -- Parses a given file for entries matching: |
| MD5 MD5_STRING_OF_HEX_CHARS FILE_NAME FILE_SIZE |
| Ignores lines that do not begin with 'MD5' and returns a |
| dict with the filenames as keys and [md5,size] as the values.""" |
| |
| if not os.path.exists(myfilename): |
| return None |
| mylines = portage_util.grabfile(myfilename, compat_level=1) |
| |
| mydigests={} |
| for x in mylines: |
| myline=string.split(x) |
| if len(myline) < 4: |
| #invalid line |
| continue |
| if myline[0] not in portage_checksum.get_valid_checksum_keys(): |
| continue |
| mykey = myline.pop(0) |
| myhash = myline.pop(0) |
| mysize = long(myline.pop()) |
| myfn = string.join(myline, " ") |
| if myfn not in mydigests: |
| mydigests[myfn] = {} |
| mydigests[myfn][mykey] = myhash |
| if "size" in mydigests[myfn]: |
| if mydigests[myfn]["size"] != mysize: |
| raise portage_exception.DigestException, "Conflicting sizes in digest: %(filename)s" % {"filename":myfilename} |
| else: |
| mydigests[myfn]["size"] = mysize |
| return mydigests |
| |
| # XXXX strict was added here to fix a missing name error. |
| # XXXX It's used below, but we're not paying attention to how we get it? |
| def digestCheckFiles(myfiles, mydigests, basedir, note="", strict=0): |
| """(fileslist, digestdict, basedir) -- Takes a list of files and a dict |
| of their digests and checks the digests against the indicated files in |
| the basedir given. Returns 1 only if all files exist and match the md5s. |
| """ |
| for x in myfiles: |
| if not mydigests.has_key(x): |
| print |
| print red("!!! No message digest entry found for file \""+x+".\"") |
| print "!!! Most likely a temporary problem. Try 'emerge sync' again later." |
| print "!!! If you are certain of the authenticity of the file then you may type" |
| print "!!! the following to generate a new digest:" |
| print "!!! ebuild /usr/portage/category/package/package-version.ebuild digest" |
| return 0 |
| myfile=os.path.normpath(basedir+"/"+x) |
| if not os.path.exists(myfile): |
| if strict: |
| print "!!! File does not exist:",myfile |
| return 0 |
| continue |
| |
| ok,reason = portage_checksum.verify_all(myfile,mydigests[x]) |
| if not ok: |
| print |
| print red("!!! Digest verification Failed:") |
| print red("!!!")+" "+str(myfile) |
| print red("!!! Reason: ")+reason |
| print |
| return 0 |
| else: |
| print ">>> md5 "+note+" ;-)",x |
| return 1 |
| |
| |
| def digestcheck(myfiles, mysettings, strict=0): |
| """Checks md5sums. Assumes all files have been downloaded.""" |
| # archive files |
| basedir=mysettings["DISTDIR"]+"/" |
| digestfn=mysettings["FILESDIR"]+"/digest-"+mysettings["PF"] |
| |
| # portage files -- p(ortagefiles)basedir |
| pbasedir=mysettings["O"]+"/" |
| manifestfn=pbasedir+"Manifest" |
| |
| if not (os.path.exists(digestfn) and os.path.exists(manifestfn)): |
| if "digest" in features: |
| print ">>> No package digest/Manifest file found." |
| print ">>> \"digest\" mode enabled; auto-generating new digest..." |
| return digestgen(myfiles,mysettings) |
| else: |
| if not os.path.exists(manifestfn): |
| if strict: |
| print red("!!! No package manifest found:"),manifestfn |
| return 0 |
| else: |
| print "--- No package manifest found:",manifestfn |
| if not os.path.exists(digestfn): |
| print "!!! No package digest file found:",digestfn |
| print "!!! Type \"ebuild foo.ebuild digest\" to generate it." |
| return 0 |
| |
| mydigests=digestParseFile(digestfn) |
| if mydigests==None: |
| print "!!! Failed to parse digest file:",digestfn |
| return 0 |
| mymdigests=digestParseFile(manifestfn) |
| if "strict" not in features: |
| # XXX: Remove this when manifests become mainstream. |
| pass |
| elif mymdigests==None: |
| print "!!! Failed to parse manifest file:",manifestfn |
| if strict: |
| return 0 |
| else: |
| # Check the portage-related files here. |
| mymfiles=listdir(pbasedir,recursive=1,filesonly=1,ignorecvs=1,EmptyOnError=1) |
| manifest_files = mymdigests.keys() |
| for x in ["Manifest", "ChangeLog", "metadata.xml"]: |
| while x in mymfiles: |
| mymfiles.remove(x) |
| while x in manifest_files: |
| manifest_files.remove(x) |
| for x in range(len(mymfiles)-1,-1,-1): |
| if mymfiles[x] in manifest_files: |
| manifest_files.remove(mymfiles[x]) |
| elif len(cvstree.apply_cvsignore_filter([mymfiles[x]]))==0: |
| # we filter here, rather then above; manifest might have files flagged by the filter. |
| # if something is returned, then it's flagged as a bad file |
| # manifest doesn't know about it, so we kill it here. |
| del mymfiles[x] |
| else: |
| print red("!!! Security Violation: A file exists that is not in the manifest.") |
| print "!!! File:",mymfiles[x] |
| if strict: |
| return 0 |
| if manifest_files and strict: |
| print red("!!! Files listed in the manifest do not exist!") |
| for x in manifest_files: |
| print x |
| return 0 |
| |
| if not digestCheckFiles(mymfiles, mymdigests, pbasedir, note="files ", strict=strict): |
| if strict: |
| print ">>> Please ensure you have sync'd properly. Please try '"+bold("emerge sync")+"' and" |
| print ">>> optionally examine the file(s) for corruption. "+bold("A sync will fix most cases.") |
| print |
| return 0 |
| else: |
| print "--- Manifest check failed. 'strict' not enabled; ignoring." |
| print |
| |
| # Just return the status, as it's the last check. |
| return digestCheckFiles(myfiles, mydigests, basedir, note="src_uri", strict=strict) |
| |
| # parse actionmap to spawn ebuild with the appropriate args |
| def spawnebuild(mydo,actionmap,mysettings,debug,alwaysdep=0,logfile=None): |
| if alwaysdep or ("noauto" not in features): |
| # process dependency first |
| if "dep" in actionmap[mydo].keys(): |
| retval=spawnebuild(actionmap[mydo]["dep"],actionmap,mysettings,debug,alwaysdep=alwaysdep,logfile=logfile) |
| if retval: |
| return retval |
| # spawn ebuild.sh |
| mycommand = EBUILD_SH_BINARY + " " |
| if selinux_enabled and ("sesandbox" in features) and (mydo in ["unpack","compile","test","install"]): |
| con=selinux.getcontext() |
| con=string.replace(con,mysettings["PORTAGE_T"],mysettings["PORTAGE_SANDBOX_T"]) |
| selinux.setexec(con) |
| retval=spawn(mycommand + mydo,mysettings,debug=debug, |
| free=actionmap[mydo]["args"][0], |
| droppriv=actionmap[mydo]["args"][1],logfile=logfile) |
| selinux.setexec(None) |
| else: |
| retval=spawn(mycommand + mydo,mysettings, debug=debug, |
| free=actionmap[mydo]["args"][0], |
| droppriv=actionmap[mydo]["args"][1],logfile=logfile) |
| return retval |
| |
| # chunked out deps for each phase, so that ebuild binary can use it |
| # to collapse targets down. |
| actionmap_deps={ |
| "depend": [], |
| "setup": [], |
| "unpack": ["setup"], |
| "compile":["unpack"], |
| "test": ["compile"], |
| "install":["test"], |
| "rpm": ["install"], |
| "package":["install"], |
| } |
| |
| |
| def eapi_is_supported(eapi): |
| return str(eapi).strip() == str(portage_const.EAPI).strip() |
| |
| |
| def doebuild(myebuild,mydo,myroot,mysettings,debug=0,listonly=0,fetchonly=0,cleanup=0,dbkey=None,use_cache=1,fetchall=0,tree="porttree"): |
| global db, actionmap_deps |
| |
| ebuild_path = os.path.abspath(myebuild) |
| pkg_dir = os.path.dirname(ebuild_path) |
| |
| if mysettings.configdict["pkg"].has_key("CATEGORY"): |
| cat = mysettings.configdict["pkg"]["CATEGORY"] |
| else: |
| cat = os.path.basename(os.path.normpath(pkg_dir+"/..")) |
| mypv = os.path.basename(ebuild_path)[:-7] |
| mycpv = cat+"/"+mypv |
| |
| mysplit=pkgsplit(mypv,silent=0) |
| if mysplit==None: |
| writemsg("!!! Error: PF is null '%s'; exiting.\n" % mypv) |
| return 1 |
| |
| if mydo != "depend": |
| # XXX: We're doing a little hack here to curtain the gvisible locking |
| # XXX: that creates a deadlock... Really need to isolate that. |
| mysettings.reset(use_cache=use_cache) |
| mysettings.setcpv(mycpv,use_cache=use_cache) |
| |
| validcommands = ["help","clean","prerm","postrm","preinst","postinst", |
| "config","setup","depend","fetch","digest", |
| "unpack","compile","test","install","rpm","qmerge","merge", |
| "package","unmerge", "manifest"] |
| |
| if mydo not in validcommands: |
| validcommands.sort() |
| writemsg("!!! doebuild: '%s' is not one of the following valid commands:" % mydo) |
| for vcount in range(len(validcommands)): |
| if vcount%6 == 0: |
| writemsg("\n!!! ") |
| writemsg(string.ljust(validcommands[vcount], 11)) |
| writemsg("\n") |
| return 1 |
| |
| if not os.path.exists(myebuild): |
| writemsg("!!! doebuild: "+str(myebuild)+" not found for "+str(mydo)+"\n") |
| return 1 |
| |
| if debug: # Otherwise it overrides emerge's settings. |
| # We have no other way to set debug... debug can't be passed in |
| # due to how it's coded... Don't overwrite this so we can use it. |
| mysettings["PORTAGE_DEBUG"]=str(debug) |
| |
| mysettings["ROOT"] = myroot |
| mysettings["STARTDIR"] = getcwd() |
| |
| mysettings["EBUILD"] = ebuild_path |
| mysettings["O"] = pkg_dir |
| mysettings["CATEGORY"] = cat |
| mysettings["FILESDIR"] = pkg_dir+"/files" |
| mysettings["PF"] = mypv |
| |
| mysettings["ECLASSDIR"] = mysettings["PORTDIR"]+"/eclass" |
| mysettings["SANDBOX_LOG"] = mycpv.replace("/", "_-_") |
| |
| mysettings["PROFILE_PATHS"] = string.join(mysettings.profiles,"\n")+"\n"+CUSTOM_PROFILE_PATH |
| mysettings["P"] = mysplit[0]+"-"+mysplit[1] |
| mysettings["PN"] = mysplit[0] |
| mysettings["PV"] = mysplit[1] |
| mysettings["PR"] = mysplit[2] |
| |
| if mydo != "depend": |
| try: |
| mysettings["INHERITED"], mysettings["RESTRICT"] = db[root][tree].dbapi.aux_get( \ |
| mycpv,["INHERITED","RESTRICT"]) |
| mysettings["PORTAGE_RESTRICT"]=string.join(flatten(portage_dep.use_reduce(portage_dep.paren_reduce( \ |
| mysettings["RESTRICT"]), uselist=mysettings["USE"].split())),' ') |
| except SystemExit, e: |
| raise |
| except: |
| pass |
| eapi = db[root][tree].dbapi.aux_get(mycpv, ["EAPI"])[0] |
| if not eapi_is_supported(eapi): |
| # can't do anything with this. |
| raise portage_exception.UnsupportedAPIException(mycpv, eapi) |
| |
| if mysplit[2] == "r0": |
| mysettings["PVR"]=mysplit[1] |
| else: |
| mysettings["PVR"]=mysplit[1]+"-"+mysplit[2] |
| |
| mysettings["SLOT"]="" |
| |
| if mysettings.has_key("PATH"): |
| mysplit=string.split(mysettings["PATH"],":") |
| else: |
| mysplit=[] |
| if PORTAGE_BIN_PATH not in mysplit: |
| mysettings["PATH"]=PORTAGE_BIN_PATH+":"+mysettings["PATH"] |
| |
| |
| mysettings["BUILD_PREFIX"] = mysettings["PORTAGE_TMPDIR"]+"/portage" |
| mysettings["HOME"] = mysettings["BUILD_PREFIX"]+"/homedir" |
| mysettings["PKG_TMPDIR"] = mysettings["PORTAGE_TMPDIR"]+"/portage-pkg" |
| mysettings["BUILDDIR"] = mysettings["BUILD_PREFIX"]+"/"+mysettings["PF"] |
| |
| mysettings["PORTAGE_BASHRC"] = EBUILD_SH_ENV_FILE |
| |
| #set up KV variable -- DEP SPEEDUP :: Don't waste time. Keep var persistent. |
| if (mydo!="depend") or not mysettings.has_key("KV"): |
| mykv,err1=ExtractKernelVersion(root+"usr/src/linux") |
| if mykv: |
| # Regular source tree |
| mysettings["KV"]=mykv |
| else: |
| mysettings["KV"]="" |
| |
| if (mydo!="depend") or not mysettings.has_key("KVERS"): |
| myso=os.uname()[2] |
| mysettings["KVERS"]=myso[1] |
| |
| |
| # get possible slot information from the deps file |
| if mydo=="depend": |
| if mysettings.has_key("PORTAGE_DEBUG") and mysettings["PORTAGE_DEBUG"]=="1": |
| # XXX: This needs to use a FD for saving the output into a file. |
| # XXX: Set this up through spawn |
| pass |
| writemsg("!!! DEBUG: dbkey: %s\n" % str(dbkey), 2) |
| if dbkey: |
| mysettings["dbkey"] = dbkey |
| else: |
| mysettings["dbkey"] = mysettings.depcachedir+"/aux_db_key_temp" |
| |
| retval = spawn(EBUILD_SH_BINARY+" depend",mysettings) |
| return retval |
| |
| logfile=None |
| # Build directory creation isn't required for any of these. |
| if mydo not in ["fetch","digest","manifest"]: |
| |
| if not os.path.exists(mysettings["BUILD_PREFIX"]): |
| os.makedirs(mysettings["BUILD_PREFIX"]) |
| if (os.getuid() == 0): |
| os.chown(mysettings["BUILD_PREFIX"],portage_uid,portage_gid) |
| os.chmod(mysettings["BUILD_PREFIX"],00775) |
| |
| # Should be ok again to set $T, as sandbox does not depend on it |
| mysettings["T"]=mysettings["BUILDDIR"]+"/temp" |
| if cleanup or mydo=="clean": |
| if os.path.exists(mysettings["T"]): |
| shutil.rmtree(mysettings["T"]) |
| if not os.path.exists(mysettings["T"]): |
| os.makedirs(mysettings["T"]) |
| if (os.getuid() == 0): |
| os.chown(mysettings["T"],portage_uid,portage_gid) |
| os.chmod(mysettings["T"],02770) |
| |
| try: # XXX: negative RESTRICT |
| if not (("nouserpriv" in string.split(mysettings["PORTAGE_RESTRICT"])) or \ |
| ("userpriv" in string.split(mysettings["PORTAGE_RESTRICT"]))): |
| if ("userpriv" in features) and (portage_uid and portage_gid): |
| if (secpass==2): |
| if os.path.exists(mysettings["HOME"]): |
| # XXX: Potentially bad, but held down by HOME replacement above. |
| spawn("rm -Rf "+mysettings["HOME"],mysettings, free=1) |
| if not os.path.exists(mysettings["HOME"]): |
| os.makedirs(mysettings["HOME"]) |
| elif ("userpriv" in features): |
| print "!!! Disabling userpriv from features... Portage UID/GID not valid." |
| del features[features.index("userpriv")] |
| except SystemExit, e: |
| raise |
| except Exception, e: |
| print "!!! Couldn't empty HOME:",mysettings["HOME"] |
| print "!!!",e |
| |
| try: |
| # no reason to check for depend since depend returns above. |
| if not os.path.exists(mysettings["BUILD_PREFIX"]): |
| os.makedirs(mysettings["BUILD_PREFIX"]) |
| if (os.getuid() == 0): |
| os.chown(mysettings["BUILD_PREFIX"],portage_uid,portage_gid) |
| if not os.path.exists(mysettings["BUILDDIR"]): |
| os.makedirs(mysettings["BUILDDIR"]) |
| if (os.getuid() == 0): |
| os.chown(mysettings["BUILDDIR"],portage_uid,portage_gid) |
| except OSError, e: |
| print "!!! File system problem. (ReadOnly? Out of space?)" |
| print "!!! Perhaps: rm -Rf",mysettings["BUILD_PREFIX"] |
| print "!!!",str(e) |
| return 1 |
| |
| try: |
| if not os.path.exists(mysettings["HOME"]): |
| os.makedirs(mysettings["HOME"]) |
| if (os.getuid() == 0): |
| os.chown(mysettings["HOME"],portage_uid,portage_gid) |
| os.chmod(mysettings["HOME"],02770) |
| except OSError, e: |
| print "!!! File system problem. (ReadOnly? Out of space?)" |
| print "!!! Failed to create fake home directory in BUILDDIR" |
| print "!!!",str(e) |
| return 1 |
| |
| try: |
| if ("ccache" in features): |
| if (not mysettings.has_key("CCACHE_DIR")) or (mysettings["CCACHE_DIR"]==""): |
| mysettings["CCACHE_DIR"]=mysettings["PORTAGE_TMPDIR"]+"/ccache" |
| if not os.path.exists(mysettings["CCACHE_DIR"]): |
| os.makedirs(mysettings["CCACHE_DIR"]) |
| mystat = os.stat(mysettings["CCACHE_DIR"]) |
| if ("userpriv" in features): |
| if mystat[stat.ST_UID] != portage_uid or ((mystat[stat.ST_MODE]&02070)!=02070): |
| writemsg("* Adjusting permissions on ccache in %s\n" % mysettings["CCACHE_DIR"]) |
| spawn("chgrp -R "+str(portage_gid)+" "+mysettings["CCACHE_DIR"], mysettings, free=1) |
| spawn("chown "+str(portage_uid)+":"+str(portage_gid)+" "+mysettings["CCACHE_DIR"], mysettings, free=1) |
| spawn("chmod -R ug+rw "+mysettings["CCACHE_DIR"], mysettings, free=1) |
| spawn("find "+mysettings["CCACHE_DIR"]+" -type d -exec chmod g+xs \{\} \;", mysettings, free=1) |
| else: |
| if mystat[stat.ST_UID] != 0 or ((mystat[stat.ST_MODE]&02070)!=02070): |
| writemsg("* Adjusting permissions on ccache in %s\n" % mysettings["CCACHE_DIR"]) |
| spawn("chgrp -R "+str(portage_gid)+" "+mysettings["CCACHE_DIR"], mysettings, free=1) |
| spawn("chown 0:"+str(portage_gid)+" "+mysettings["CCACHE_DIR"], mysettings, free=1) |
| spawn("chmod -R ug+rw "+mysettings["CCACHE_DIR"], mysettings, free=1) |
| spawn("find "+mysettings["CCACHE_DIR"]+" -type d -exec chmod g+xs \{\} \;", mysettings, free=1) |
| except OSError, e: |
| print "!!! File system problem. (ReadOnly? Out of space?)" |
| print "!!! Perhaps: rm -Rf",mysettings["BUILD_PREFIX"] |
| print "!!!",str(e) |
| return 1 |
| |
| #try: |
| # mystat=os.stat(mysettings["CCACHE_DIR"]) |
| # if (mystat[stat.ST_GID]!=portage_gid) or ((mystat[stat.ST_MODE]&02070)!=02070): |
| # print "*** Adjusting ccache permissions for portage user..." |
| # os.chown(mysettings["CCACHE_DIR"],portage_uid,portage_gid) |
| # os.chmod(mysettings["CCACHE_DIR"],02770) |
| # spawn("chown -R "+str(portage_uid)+":"+str(portage_gid)+" "+mysettings["CCACHE_DIR"],mysettings, free=1) |
| # spawn("chmod -R g+rw "+mysettings["CCACHE_DIR"],mysettings, free=1) |
| #except SystemExit, e: |
| # raise |
| #except: |
| # pass |
| |
| if "distcc" in features: |
| try: |
| if (not mysettings.has_key("DISTCC_DIR")) or (mysettings["DISTCC_DIR"]==""): |
| mysettings["DISTCC_DIR"]=mysettings["PORTAGE_TMPDIR"]+"/portage/.distcc" |
| if not os.path.exists(mysettings["DISTCC_DIR"]): |
| os.makedirs(mysettings["DISTCC_DIR"]) |
| os.chown(mysettings["DISTCC_DIR"],portage_uid,portage_gid) |
| os.chmod(mysettings["DISTCC_DIR"],02775) |
| for x in ("/lock", "/state"): |
| if not os.path.exists(mysettings["DISTCC_DIR"]+x): |
| os.mkdir(mysettings["DISTCC_DIR"]+x) |
| os.chown(mysettings["DISTCC_DIR"]+x,portage_uid,portage_gid) |
| os.chmod(mysettings["DISTCC_DIR"]+x,02775) |
| except OSError, e: |
| writemsg("\n!!! File system problem when setting DISTCC_DIR directory permissions.\n") |
| writemsg( "!!! DISTCC_DIR="+str(mysettings["DISTCC_DIR"]+"\n")) |
| writemsg( "!!! "+str(e)+"\n\n") |
| time.sleep(5) |
| features.remove("distcc") |
| mysettings["DISTCC_DIR"]="" |
| |
| mysettings["WORKDIR"]=mysettings["BUILDDIR"]+"/work" |
| mysettings["D"]=mysettings["BUILDDIR"]+"/image/" |
| |
| if mysettings.has_key("PORT_LOGDIR"): |
| if os.access(mysettings["PORT_LOGDIR"]+"/",os.W_OK): |
| try: |
| os.chown(mysettings["PORT_LOGDIR"],portage_uid,portage_gid) |
| os.chmod(mysettings["PORT_LOGDIR"],02770) |
| if not mysettings.has_key("LOG_PF") or (mysettings["LOG_PF"] != mysettings["PF"]): |
| mysettings["LOG_PF"]=mysettings["PF"] |
| mysettings["LOG_COUNTER"]=str(db[myroot]["vartree"].dbapi.get_counter_tick_core("/")) |
| logfile="%s/%s-%s.log" % (mysettings["PORT_LOGDIR"],mysettings["LOG_COUNTER"],mysettings["LOG_PF"]) |
| except OSError, e: |
| mysettings["PORT_LOGDIR"]="" |
| print "!!! Unable to chown/chmod PORT_LOGDIR. Disabling logging." |
| print "!!!",e |
| else: |
| print "!!! Cannot create log... No write access / Does not exist" |
| print "!!! PORT_LOGDIR:",mysettings["PORT_LOGDIR"] |
| mysettings["PORT_LOGDIR"]="" |
| |
| if mydo=="unmerge": |
| return unmerge(mysettings["CATEGORY"],mysettings["PF"],myroot,mysettings) |
| |
| # if any of these are being called, handle them -- running them out of the sandbox -- and stop now. |
| if mydo=="clean": |
| logfile=None |
| if mydo in ["help","clean","setup"]: |
| return spawn(EBUILD_SH_BINARY+" "+mydo,mysettings,debug=debug,free=1,logfile=logfile) |
| elif mydo in ["prerm","postrm","preinst","postinst","config"]: |
| mysettings.load_infodir(pkg_dir) |
| return spawn(EBUILD_SH_BINARY+" "+mydo,mysettings,debug=debug,free=1,logfile=logfile) |
| |
| try: |
| mysettings["SLOT"],mysettings["RESTRICT"] = db["/"]["porttree"].dbapi.aux_get(mycpv,["SLOT","RESTRICT"]) |
| except (IOError,KeyError): |
| print red("doebuild():")+" aux_get() error reading "+mycpv+"; aborting." |
| sys.exit(1) |
| |
| newuris, alist = db["/"]["porttree"].dbapi.getfetchlist(mycpv,mysettings=mysettings) |
| alluris, aalist = db["/"]["porttree"].dbapi.getfetchlist(mycpv,mysettings=mysettings,all=1) |
| mysettings["A"]=string.join(alist," ") |
| mysettings["AA"]=string.join(aalist," ") |
| if ("mirror" in features) or fetchall: |
| fetchme=alluris[:] |
| checkme=aalist[:] |
| elif mydo=="digest": |
| fetchme=alluris[:] |
| checkme=aalist[:] |
| digestfn=mysettings["FILESDIR"]+"/digest-"+mysettings["PF"] |
| if os.path.exists(digestfn): |
| mydigests=digestParseFile(digestfn) |
| if mydigests: |
| for x in mydigests: |
| while x in checkme: |
| i = checkme.index(x) |
| del fetchme[i] |
| del checkme[i] |
| else: |
| fetchme=newuris[:] |
| checkme=alist[:] |
| |
| try: |
| if not os.path.exists(mysettings["DISTDIR"]): |
| os.makedirs(mysettings["DISTDIR"]) |
| if not os.path.exists(mysettings["DISTDIR"]+"/cvs-src"): |
| os.makedirs(mysettings["DISTDIR"]+"/cvs-src") |
| except OSError, e: |
| print "!!! File system problem. (Bad Symlink?)" |
| print "!!! Fetching may fail:",str(e) |
| |
| try: |
| mystat=os.stat(mysettings["DISTDIR"]+"/cvs-src") |
| if ((mystat[stat.ST_GID]!=portage_gid) or ((mystat[stat.ST_MODE]&02770)!=02770)) and not listonly: |
| print "*** Adjusting cvs-src permissions for portage user..." |
| os.chown(mysettings["DISTDIR"]+"/cvs-src",0,portage_gid) |
| os.chmod(mysettings["DISTDIR"]+"/cvs-src",02770) |
| spawn("chgrp -R "+str(portage_gid)+" "+mysettings["DISTDIR"]+"/cvs-src", free=1) |
| spawn("chmod -R g+rw "+mysettings["DISTDIR"]+"/cvs-src", free=1) |
| except SystemExit, e: |
| raise |
| except: |
| pass |
| |
| if mydo!="manifest" and not fetch(fetchme, mysettings, listonly=listonly, fetchonly=fetchonly): |
| return 1 |
| |
| if mydo=="fetch" and listonly: |
| return 0 |
| |
| if "digest" in features: |
| #generate digest if it doesn't exist. |
| if mydo=="digest": |
| return (not digestgen(aalist,mysettings,overwrite=1)) |
| else: |
| digestgen(aalist,mysettings,overwrite=0) |
| elif mydo=="digest": |
| #since we are calling "digest" directly, recreate the digest even if it already exists |
| return (not digestgen(aalist,mysettings,overwrite=1)) |
| if mydo=="manifest": |
| return (not digestgen(aalist,mysettings,overwrite=1,manifestonly=1)) |
| |
| if not digestcheck(checkme, mysettings, ("strict" in features)): |
| return 1 |
| |
| if mydo=="fetch": |
| return 0 |
| |
| #initial dep checks complete; time to process main commands |
| |
| nosandbox=(("userpriv" in features) and ("usersandbox" not in features) and \ |
| ("userpriv" not in mysettings["RESTRICT"]) and ("nouserpriv" not in mysettings["RESTRICT"])) |
| if nosandbox and ("userpriv" not in features or "userpriv" in mysettings["RESTRICT"] or \ |
| "nouserpriv" in mysettings["RESTRICT"]): |
| nosandbox = ("sandbox" not in features and "usersandbox" not in features) |
| |
| actionmap = { |
| "depend": {"args":(0,1)}, # sandbox / portage |
| "setup": {"args":(1,0)}, # without / root |
| "unpack": {"args":(0,1)}, # sandbox / portage |
| "compile":{"args":(nosandbox,1)}, # optional / portage |
| "test": {"args":(nosandbox,1)}, # optional / portage |
| "install":{"args":(0,0)}, # sandbox / root |
| "rpm": {"args":(0,0)}, # sandbox / root |
| "package":{"args":(0,0)}, # sandbox / root |
| } |
| |
| # merge the deps in so we have again a 'full' actionmap |
| # be glad when this can die. |
| for x in actionmap.keys(): |
| if len(actionmap_deps.get(x, [])): |
| actionmap[x]["dep"] = ' '.join(actionmap_deps[x]) |
| |
| if mydo in actionmap.keys(): |
| if mydo=="package": |
| for x in ["","/"+mysettings["CATEGORY"],"/All"]: |
| if not os.path.exists(mysettings["PKGDIR"]+x): |
| os.makedirs(mysettings["PKGDIR"]+x) |
| # REBUILD CODE FOR TBZ2 --- XXXX |
| return spawnebuild(mydo,actionmap,mysettings,debug,logfile=logfile) |
| elif mydo=="qmerge": |
| #check to ensure install was run. this *only* pops up when users forget it and are using ebuild |
| if not os.path.exists(mysettings["BUILDDIR"]+"/.installed"): |
| print "!!! mydo=qmerge, but install phase hasn't been ran" |
| sys.exit(1) |
| #qmerge is specifically not supposed to do a runtime dep check |
| return merge(mysettings["CATEGORY"],mysettings["PF"],mysettings["D"],mysettings["BUILDDIR"]+"/build-info",myroot,mysettings,myebuild=mysettings["EBUILD"]) |
| elif mydo=="merge": |
| retval=spawnebuild("install",actionmap,mysettings,debug,alwaysdep=1,logfile=logfile) |
| if retval: |
| return retval |
| return merge(mysettings["CATEGORY"],mysettings["PF"],mysettings["D"],mysettings["BUILDDIR"]+"/build-info",myroot,mysettings,myebuild=mysettings["EBUILD"]) |
| else: |
| print "!!! Unknown mydo:",mydo |
| sys.exit(1) |
| |
| expandcache={} |
| |
| def movefile(src,dest,newmtime=None,sstat=None,mysettings=None): |
| """moves a file from src to dest, preserving all permissions and attributes; mtime will |
| be preserved even when moving across filesystems. Returns true on success and false on |
| failure. Move is atomic.""" |
| #print "movefile("+str(src)+","+str(dest)+","+str(newmtime)+","+str(sstat)+")" |
| global lchown |
| |
| try: |
| if not sstat: |
| sstat=os.lstat(src) |
| if bsd_chflags: |
| sflags=bsd_chflags.lgetflags(src) |
| if sflags < 0: |
| # Problem getting flags... |
| writemsg("!!! Couldn't get flags for "+dest+"\n") |
| return None |
| |
| except SystemExit, e: |
| raise |
| except Exception, e: |
| print "!!! Stating source file failed... movefile()" |
| print "!!!",e |
| return None |
| |
| destexists=1 |
| try: |
| dstat=os.lstat(dest) |
| except SystemExit, e: |
| raise |
| except: |
| dstat=os.lstat(os.path.dirname(dest)) |
| destexists=0 |
| |
| if bsd_chflags: |
| # Check that we can actually unset schg etc flags... |
| # Clear the flags on source and destination; we'll reinstate them after merging |
| if(destexists): |
| if bsd_chflags.lchflags(dest, 0) < 0: |
| writemsg("!!! Couldn't clear flags on file being merged: \n ") |
| # We might have an immutable flag on the parent dir; save and clear. |
| pflags=bsd_chflags.lgetflags(os.path.dirname(dest)) |
| bsd_chflags.lchflags(os.path.dirname(dest), 0) |
| |
| # Don't bother checking the return value here; if it fails then the next line will catch it. |
| bsd_chflags.lchflags(src, 0) |
| |
| if bsd_chflags.lhasproblems(src)>0 or (destexists and bsd_chflags.lhasproblems(dest)>0) or bsd_chflags.lhasproblems(os.path.dirname(dest))>0: |
| # This is bad: we can't merge the file with these flags set. |
| writemsg("!!! Can't merge file "+dest+" because of flags set\n") |
| return None |
| |
| if destexists: |
| if stat.S_ISLNK(dstat[stat.ST_MODE]): |
| try: |
| os.unlink(dest) |
| destexists=0 |
| except SystemExit, e: |
| raise |
| except Exception, e: |
| pass |
| |
| if stat.S_ISLNK(sstat[stat.ST_MODE]): |
| try: |
| target=os.readlink(src) |
| if mysettings and mysettings["D"]: |
| if target.find(mysettings["D"])==0: |
| target=target[len(mysettings["D"]):] |
| if destexists and not stat.S_ISDIR(dstat[stat.ST_MODE]): |
| os.unlink(dest) |
| if selinux_enabled: |
| sid = selinux.get_lsid(src) |
| selinux.secure_symlink(target,dest,sid) |
| else: |
| os.symlink(target,dest) |
| lchown(dest,sstat[stat.ST_UID],sstat[stat.ST_GID]) |
| if bsd_chflags: |
| # Restore the flags we saved before moving |
| if bsd_chflags.lchflags(dest, sflags) < 0 or bsd_chflags.lchflags(os.path.dirname(dest), pflags) < 0: |
| writemsg("!!! Couldn't restore flags ("+str(flags)+") on " + dest+":\n") |
| writemsg("!!! %s\n" % str(e)) |
| return None |
| return os.lstat(dest)[stat.ST_MTIME] |
| except SystemExit, e: |
| raise |
| except Exception, e: |
| print "!!! failed to properly create symlink:" |
| print "!!!",dest,"->",target |
| print "!!!",e |
| return None |
| |
| renamefailed=1 |
| if sstat[stat.ST_DEV]==dstat[stat.ST_DEV] or selinux_enabled: |
| try: |
| if selinux_enabled: |
| ret=selinux.secure_rename(src,dest) |
| else: |
| ret=os.rename(src,dest) |
| renamefailed=0 |
| except SystemExit, e: |
| raise |
| except Exception, e: |
| import errno |
| if e[0]!=errno.EXDEV: |
| # Some random error. |
| print "!!! Failed to move",src,"to",dest |
| print "!!!",e |
| return None |
| # Invalid cross-device-link 'bind' mounted or actually Cross-Device |
| if renamefailed: |
| didcopy=0 |
| if stat.S_ISREG(sstat[stat.ST_MODE]): |
| try: # For safety copy then move it over. |
| if selinux_enabled: |
| selinux.secure_copy(src,dest+"#new") |
| selinux.secure_rename(dest+"#new",dest) |
| else: |
| shutil.copyfile(src,dest+"#new") |
| os.rename(dest+"#new",dest) |
| didcopy=1 |
| except SystemExit, e: |
| raise |
| except Exception, e: |
| print '!!! copy',src,'->',dest,'failed.' |
| print "!!!",e |
| return None |
| else: |
| #we don't yet handle special, so we need to fall back to /bin/mv |
| if selinux_enabled: |
| a=commands.getstatusoutput(MOVE_BINARY+" -c -f "+"'"+src+"' '"+dest+"'") |
| else: |
| a=commands.getstatusoutput(MOVE_BINARY+" -f "+"'"+src+"' '"+dest+"'") |
| if a[0]!=0: |
| print "!!! Failed to move special file:" |
| print "!!! '"+src+"' to '"+dest+"'" |
| print "!!!",a |
| return None # failure |
| try: |
| if didcopy: |
| if stat.S_ISLNK(sstat[stat.ST_MODE]): |
| lchown(dest,sstat[stat.ST_UID],sstat[stat.ST_GID]) |
| else: |
| os.chown(dest,sstat[stat.ST_UID],sstat[stat.ST_GID]) |
| os.chmod(dest, stat.S_IMODE(sstat[stat.ST_MODE])) # Sticky is reset on chown |
| os.unlink(src) |
| except SystemExit, e: |
| raise |
| except Exception, e: |
| print "!!! Failed to chown/chmod/unlink in movefile()" |
| print "!!!",dest |
| print "!!!",e |
| return None |
| |
| if newmtime: |
| os.utime(dest,(newmtime,newmtime)) |
| else: |
| os.utime(dest, (sstat[stat.ST_ATIME], sstat[stat.ST_MTIME])) |
| newmtime=sstat[stat.ST_MTIME] |
| |
| if bsd_chflags: |
| # Restore the flags we saved before moving |
| if bsd_chflags.lchflags(dest, sflags) < 0 or bsd_chflags.lchflags(os.path.dirname(dest), pflags) < 0: |
| writemsg("!!! Couldn't restore flags ("+str(sflags)+") on " + dest+":\n") |
| return None |
| |
| return newmtime |
| |
| def merge(mycat,mypkg,pkgloc,infloc,myroot,mysettings,myebuild=None): |
| mylink=dblink(mycat,mypkg,myroot,mysettings) |
| return mylink.merge(pkgloc,infloc,myroot,myebuild) |
| |
| def unmerge(cat,pkg,myroot,mysettings,mytrimworld=1): |
| mylink=dblink(cat,pkg,myroot,mysettings) |
| if mylink.exists(): |
| mylink.unmerge(trimworld=mytrimworld,cleanup=1) |
| mylink.delete() |
| |
| def relparse(myver): |
| "converts last version part into three components" |
| number=0 |
| suffix=0 |
| endtype=0 |
| endnumber=0 |
| |
| mynewver=string.split(myver,"_") |
| myver=mynewver[0] |
| |
| #normal number or number with letter at end |
| divider=len(myver)-1 |
| if myver[divider:] not in "1234567890": |
| #letter at end |
| suffix=ord(myver[divider:]) |
| number=string.atof(myver[0:divider]) |
| else: |
| number=string.atof(myver) |
| |
| if len(mynewver)==2: |
| #an endversion |
| for x in endversion_keys: |
| elen=len(x) |
| if mynewver[1][:elen] == x: |
| match=1 |
| endtype=endversion[x] |
| try: |
| endnumber=string.atof(mynewver[1][elen:]) |
| except SystemExit, e: |
| raise |
| except: |
| endnumber=0 |
| break |
| return [number,suffix,endtype,endnumber] |
| |
| #returns 1 if valid version string, else 0 |
| # valid string in format: <v1>.<v2>...<vx>[a-z,_{endversion}[vy]] |
| # ververify doesn't do package rev. |
| |
| vercache={} |
| def ververify(myorigval,silent=1): |
| try: |
| return vercache[myorigval] |
| except KeyError: |
| pass |
| if len(myorigval)==0: |
| if not silent: |
| print "!!! Name error: package contains empty \"-\" part." |
| return 0 |
| myval=string.split(myorigval,'.') |
| if len(myval)==0: |
| if not silent: |
| print "!!! Name error: empty version string." |
| vercache[myorigval]=0 |
| return 0 |
| #all but the last version must be a numeric |
| for x in myval[:-1]: |
| if not len(x): |
| if not silent: |
| print "!!! Name error in",myorigval+": two decimal points in a row" |
| vercache[myorigval]=0 |
| return 0 |
| try: |
| foo=int(x) |
| except SystemExit, e: |
| raise |
| except: |
| if not silent: |
| print "!!! Name error in",myorigval+": \""+x+"\" is not a valid version component." |
| vercache[myorigval]=0 |
| return 0 |
| if not len(myval[-1]): |
| if not silent: |
| print "!!! Name error in",myorigval+": two decimal points in a row" |
| vercache[myorigval]=0 |
| return 0 |
| try: |
| foo=int(myval[-1]) |
| vercache[myorigval]=1 |
| return 1 |
| except SystemExit, e: |
| raise |
| except: |
| pass |
| #ok, our last component is not a plain number or blank, let's continue |
| if myval[-1][-1] in string.lowercase: |
| try: |
| foo=int(myval[-1][:-1]) |
| vercache[myorigval]=1 |
| return 1 |
| # 1a, 2.0b, etc. |
| except SystemExit, e: |
| raise |
| except: |
| pass |
| #ok, maybe we have a 1_alpha or 1_beta2; let's see |
| #ep="endpart" |
| ep=string.split(myval[-1],"_") |
| if len(ep)!=2: |
| if not silent: |
| print "!!! Name error in",myorigval |
| vercache[myorigval]=0 |
| return 0 |
| try: |
| foo=int(ep[0][-1]) |
| chk=ep[0] |
| except SystemExit, e: |
| raise |
| except: |
| # because it's ok last char is not numeric. example: foo-1.0.0a_pre1 |
| chk=ep[0][:-1] |
| |
| try: |
| foo=int(chk) |
| except SystemExit, e: |
| raise |
| except: |
| #this needs to be numeric or numeric+single letter, |
| #i.e. the "1" in "1_alpha" or "1a_alpha" |
| if not silent: |
| print "!!! Name error in",myorigval+": characters before _ must be numeric or numeric+single letter" |
| vercache[myorigval]=0 |
| return 0 |
| for mye in endversion_keys: |
| if ep[1][0:len(mye)]==mye: |
| if len(mye)==len(ep[1]): |
| #no trailing numeric; ok |
| vercache[myorigval]=1 |
| return 1 |
| else: |
| try: |
| foo=int(ep[1][len(mye):]) |
| vercache[myorigval]=1 |
| return 1 |
| except SystemExit, e: |
| raise |
| except: |
| #if no endversions work, *then* we return 0 |
| pass |
| if not silent: |
| print "!!! Name error in",myorigval |
| vercache[myorigval]=0 |
| return 0 |
| |
| def isvalidatom(atom): |
| mycpv_cps = catpkgsplit(dep_getcpv(atom)) |
| operator = get_operator(atom) |
| if operator: |
| if operator[0] in "<>" and atom[-1] == "*": |
| return 0 |
| if mycpv_cps and mycpv_cps[0] != "null": |
| # >=cat/pkg-1.0 |
| return 1 |
| else: |
| # >=cat/pkg or >=pkg-1.0 (no category) |
| return 0 |
| if mycpv_cps: |
| # cat/pkg-1.0 |
| return 0 |
| |
| if (len(string.split(atom, '/'))==2): |
| # cat/pkg |
| return 1 |
| else: |
| return 0 |
| |
| def isjustname(mypkg): |
| myparts=string.split(mypkg,'-') |
| for x in myparts: |
| if ververify(x): |
| return 0 |
| return 1 |
| |
| iscache={} |
| def isspecific(mypkg): |
| "now supports packages with no category" |
| try: |
| return iscache[mypkg] |
| except SystemExit, e: |
| raise |
| except: |
| pass |
| mysplit=string.split(mypkg,"/") |
| if not isjustname(mysplit[-1]): |
| iscache[mypkg]=1 |
| return 1 |
| iscache[mypkg]=0 |
| return 0 |
| |
| # This function can be used as a package verification function, i.e. |
| # "pkgsplit("foo-1.2-1") will return None if foo-1.2-1 isn't a valid |
| # package (with version) name. If it is a valid name, pkgsplit will |
| # return a list containing: [ pkgname, pkgversion(norev), pkgrev ]. |
| # For foo-1.2-1, this list would be [ "foo", "1.2", "1" ]. For |
| # Mesa-3.0, this list would be [ "Mesa", "3.0", "0" ]. |
| pkgcache={} |
| |
| def pkgsplit(mypkg,silent=1): |
| try: |
| if not pkgcache[mypkg]: |
| return None |
| return pkgcache[mypkg][:] |
| except KeyError: |
| pass |
| myparts=string.split(mypkg,'-') |
| if len(myparts)<2: |
| if not silent: |
| print "!!! Name error in",mypkg+": missing a version or name part." |
| pkgcache[mypkg]=None |
| return None |
| for x in myparts: |
| if len(x)==0: |
| if not silent: |
| print "!!! Name error in",mypkg+": empty \"-\" part." |
| pkgcache[mypkg]=None |
| return None |
| #verify rev |
| revok=0 |
| myrev=myparts[-1] |
| if len(myrev) and myrev[0]=="r": |
| try: |
| int(myrev[1:]) |
| revok=1 |
| except SystemExit, e: |
| raise |
| except: |
| pass |
| if revok: |
| if ververify(myparts[-2]): |
| if len(myparts)==2: |
| pkgcache[mypkg]=None |
| return None |
| else: |
| for x in myparts[:-2]: |
| if ververify(x): |
| pkgcache[mypkg]=None |
| return None |
| #names can't have versiony looking parts |
| myval=[string.join(myparts[:-2],"-"),myparts[-2],myparts[-1]] |
| pkgcache[mypkg]=myval |
| return myval |
| else: |
| pkgcache[mypkg]=None |
| return None |
| |
| elif ververify(myparts[-1],silent=silent): |
| if len(myparts)==1: |
| if not silent: |
| print "!!! Name error in",mypkg+": missing name part." |
| pkgcache[mypkg]=None |
| return None |
| else: |
| for x in myparts[:-1]: |
| if ververify(x): |
| if not silent: |
| print "!!! Name error in",mypkg+": multiple version parts." |
| pkgcache[mypkg]=None |
| return None |
| myval=[string.join(myparts[:-1],"-"),myparts[-1],"r0"] |
| pkgcache[mypkg]=myval[:] |
| return myval |
| else: |
| pkgcache[mypkg]=None |
| return None |
| |
| def getCPFromCPV(mycpv): |
| """Calls pkgsplit on a cpv and returns only the cp.""" |
| return pkgsplit(mycpv)[0] |
| |
| catcache={} |
| def catpkgsplit(mydata,silent=1): |
| "returns [cat, pkgname, version, rev ]" |
| try: |
| if not catcache[mydata]: |
| return None |
| return catcache[mydata][:] |
| except KeyError: |
| pass |
| mysplit=mydata.split("/") |
| p_split=None |
| if len(mysplit)==1: |
| retval=["null"] |
| p_split=pkgsplit(mydata,silent=silent) |
| elif len(mysplit)==2: |
| retval=[mysplit[0]] |
| p_split=pkgsplit(mysplit[1],silent=silent) |
| if not p_split: |
| catcache[mydata]=None |
| return None |
| retval.extend(p_split) |
| catcache[mydata]=retval |
| return retval |
| |
| # vercmp: |
| # This takes two version strings and returns an integer to tell you whether |
| # the versions are the same, val1>val2 or val2>val1. |
| vcmpcache={} |
| def vercmp(val1,val2): |
| if val1==val2: |
| #quick short-circuit |
| return 0 |
| valkey=val1+" "+val2 |
| try: |
| return vcmpcache[valkey] |
| try: |
| return -vcmpcache[val2+" "+val1] |
| except KeyError: |
| pass |
| except KeyError: |
| pass |
| |
| # consider 1_p2 vc 1.1 |
| # after expansion will become (1_p2,0) vc (1,1) |
| # then 1_p2 is compared with 1 before 0 is compared with 1 |
| # to solve the bug we need to convert it to (1,0_p2) |
| # by splitting _prepart part and adding it back _after_expansion |
| val1_prepart = val2_prepart = '' |
| if val1.count('_'): |
| val1, val1_prepart = val1.split('_', 1) |
| if val2.count('_'): |
| val2, val2_prepart = val2.split('_', 1) |
| |
| # replace '-' by '.' |
| # FIXME: Is it needed? can val1/2 contain '-'? |
| val1=string.split(val1,'-') |
| if len(val1)==2: |
| val1[0]=val1[0]+"."+val1[1] |
| val2=string.split(val2,'-') |
| if len(val2)==2: |
| val2[0]=val2[0]+"."+val2[1] |
| |
| val1=string.split(val1[0],'.') |
| val2=string.split(val2[0],'.') |
| |
| #add back decimal point so that .03 does not become "3" ! |
| for x in range(1,len(val1)): |
| if val1[x][0] == '0' : |
| val1[x]='.' + val1[x] |
| for x in range(1,len(val2)): |
| if val2[x][0] == '0' : |
| val2[x]='.' + val2[x] |
| |
| # extend version numbers |
| if len(val2)<len(val1): |
| val2.extend(["0"]*(len(val1)-len(val2))) |
| elif len(val1)<len(val2): |
| val1.extend(["0"]*(len(val2)-len(val1))) |
| |
| # add back _prepart tails |
| if val1_prepart: |
| val1[-1] += '_' + val1_prepart |
| if val2_prepart: |
| val2[-1] += '_' + val2_prepart |
| #The above code will extend version numbers out so they |
| #have the same number of digits. |
| for x in range(0,len(val1)): |
| cmp1=relparse(val1[x]) |
| cmp2=relparse(val2[x]) |
| for y in range(0,4): |
| myret=cmp1[y]-cmp2[y] |
| if myret != 0: |
| vcmpcache[valkey]=myret |
| return myret |
| vcmpcache[valkey]=0 |
| return 0 |
| |
| |
| def pkgcmp(pkg1,pkg2): |
| """if returnval is less than zero, then pkg2 is newer than pkg1, zero if equal and positive if older.""" |
| if pkg1[0] != pkg2[0]: |
| return None |
| mycmp=vercmp(pkg1[1],pkg2[1]) |
| if mycmp>0: |
| return 1 |
| if mycmp<0: |
| return -1 |
| r1=int(pkg1[2][1:]) |
| r2=int(pkg2[2][1:]) |
| if r1>r2: |
| return 1 |
| if r2>r1: |
| return -1 |
| return 0 |
| |
| def dep_parenreduce(mysplit,mypos=0): |
| "Accepts a list of strings, and converts '(' and ')' surrounded items to sub-lists" |
| while (mypos<len(mysplit)): |
| if (mysplit[mypos]=="("): |
| firstpos=mypos |
| mypos=mypos+1 |
| while (mypos<len(mysplit)): |
| if mysplit[mypos]==")": |
| mysplit[firstpos:mypos+1]=[mysplit[firstpos+1:mypos]] |
| mypos=firstpos |
| break |
| elif mysplit[mypos]=="(": |
| #recurse |
| mysplit=dep_parenreduce(mysplit,mypos=mypos) |
| mypos=mypos+1 |
| mypos=mypos+1 |
| return mysplit |
| |
| def dep_opconvert(mysplit,myuse,mysettings): |
| "Does dependency operator conversion" |
| |
| #check_config_instance(mysettings) |
| |
| mypos=0 |
| newsplit=[] |
| while mypos<len(mysplit): |
| if type(mysplit[mypos])==types.ListType: |
| newsplit.append(dep_opconvert(mysplit[mypos],myuse,mysettings)) |
| mypos += 1 |
| elif mysplit[mypos]==")": |
| #mismatched paren, error |
| return None |
| elif mysplit[mypos]=="||": |
| if ((mypos+1)>=len(mysplit)) or (type(mysplit[mypos+1])!=types.ListType): |
| # || must be followed by paren'd list |
| return None |
| try: |
| mynew=dep_opconvert(mysplit[mypos+1],myuse,mysettings) |
| except SystemExit, e: |
| raise |
| except Exception, e: |
| print "!!! Unable to satisfy OR dependency:",string.join(mysplit," || ") |
| raise e |
| mynew[0:0]=["||"] |
| newsplit.append(mynew) |
| mypos += 2 |
| elif mysplit[mypos][-1]=="?": |
| #uses clause, i.e "gnome? ( foo bar )" |
| #this is a quick and dirty hack so that repoman can enable all USE vars: |
| if (len(myuse)==1) and (myuse[0]=="*") and mysettings: |
| # enable it even if it's ! (for repoman) but kill it if it's |
| # an arch variable that isn't for this arch. XXX Sparc64? |
| k=mysplit[mypos][:-1] |
| if k[0]=="!": |
| k=k[1:] |
| if k not in archlist and k not in mysettings.usemask: |
| enabled=1 |
| elif k in archlist: |
| if k==mysettings["ARCH"]: |
| if mysplit[mypos][0]=="!": |
| enabled=0 |
| else: |
| enabled=1 |
| elif mysplit[mypos][0]=="!": |
| enabled=1 |
| else: |
| enabled=0 |
| else: |
| enabled=0 |
| else: |
| if mysplit[mypos][0]=="!": |
| myusevar=mysplit[mypos][1:-1] |
| if myusevar in myuse: |
| enabled=0 |
| else: |
| enabled=1 |
| else: |
| myusevar=mysplit[mypos][:-1] |
| if myusevar in myuse: |
| enabled=1 |
| else: |
| enabled=0 |
| if (mypos+2<len(mysplit)) and (mysplit[mypos+2]==":"): |
| #colon mode |
| if enabled: |
| #choose the first option |
| if type(mysplit[mypos+1])==types.ListType: |
| newsplit.append(dep_opconvert(mysplit[mypos+1],myuse,mysettings)) |
| else: |
| newsplit.append(mysplit[mypos+1]) |
| else: |
| #choose the alternate option |
| if type(mysplit[mypos+1])==types.ListType: |
| newsplit.append(dep_opconvert(mysplit[mypos+3],myuse,mysettings)) |
| else: |
| newsplit.append(mysplit[mypos+3]) |
| mypos += 4 |
| else: |
| #normal use mode |
| if enabled: |
| if type(mysplit[mypos+1])==types.ListType: |
| newsplit.append(dep_opconvert(mysplit[mypos+1],myuse,mysettings)) |
| else: |
| newsplit.append(mysplit[mypos+1]) |
| #otherwise, continue. |
| mypos += 2 |
| else: |
| #normal item |
| newsplit.append(mysplit[mypos]) |
| mypos += 1 |
| return newsplit |
| |
| def dep_virtual(mysplit, mysettings): |
| "Does virtual dependency conversion" |
| |
| |
| |
| newsplit=[] |
| for x in mysplit: |
| if type(x)==types.ListType: |
| newsplit.append(dep_virtual(x, mysettings)) |
| else: |
| mykey=dep_getkey(x) |
| if mysettings.virtuals.has_key(mykey): |
| if len(mysettings.virtuals[mykey])==1: |
| a=string.replace(x, mykey, mysettings.virtuals[mykey][0]) |
| else: |
| if x[0]=="!": |
| # blocker needs "and" not "or(||)". |
| a=[] |
| else: |
| a=['||'] |
| for y in mysettings.virtuals[mykey]: |
| a.append(string.replace(x, mykey, y)) |
| newsplit.append(a) |
| else: |
| newsplit.append(x) |
| return newsplit |
| |
| def dep_eval(deplist): |
| if len(deplist)==0: |
| return 1 |
| if deplist[0]=="||": |
| #or list; we just need one "1" |
| for x in deplist[1:]: |
| if type(x)==types.ListType: |
| if dep_eval(x)==1: |
| return 1 |
| elif x==1: |
| return 1 |
| return 0 |
| else: |
| for x in deplist: |
| if type(x)==types.ListType: |
| if dep_eval(x)==0: |
| return 0 |
| elif x==0 or x==2: |
| return 0 |
| return 1 |
| |
| def dep_zapdeps(unreduced,reduced,vardbapi=None,use_binaries=0): |
| """Takes an unreduced and reduced deplist and removes satisfied dependencies. |
| Returned deplist contains steps that must be taken to satisfy dependencies.""" |
| writemsg("ZapDeps -- %s\n" % (use_binaries), 2) |
| if unreduced==[] or unreduced==['||'] : |
| return [] |
| if unreduced[0]=="||": |
| if dep_eval(reduced): |
| #deps satisfied, return empty list. |
| return [] |
| else: |
| #try to find an installed dep. |
| ### We use fakedb when --update now, so we can't use local vardbapi here. |
| ### This should be fixed in the feature. |
| ### see bug 45468. |
| ##if vardbapi: |
| ## mydbapi=vardbapi |
| ##else: |
| ## mydbapi=db[root]["vartree"].dbapi |
| mydbapi=db[root]["vartree"].dbapi |
| |
| if db["/"].has_key("porttree"): |
| myportapi=db["/"]["porttree"].dbapi |
| else: |
| myportapi=None |
| |
| if use_binaries and db["/"].has_key("bintree"): |
| mybinapi=db["/"]["bintree"].dbapi |
| writemsg("Using bintree...\n",2) |
| else: |
| mybinapi=None |
| |
| x=1 |
| candidate=[] |
| while x<len(reduced): |
| writemsg("x: %s, reduced[x]: %s\n" % (x,reduced[x]), 2) |
| if (type(reduced[x])==types.ListType): |
| newcand = dep_zapdeps(unreduced[x], reduced[x], vardbapi=vardbapi, use_binaries=use_binaries) |
| candidate.append(newcand) |
| else: |
| if (reduced[x]==False): |
| candidate.append([unreduced[x]]) |
| else: |
| candidate.append([]) |
| x+=1 |
| |
| #use installed and no-masked package(s) in portage. |
| for x in candidate: |
| match=1 |
| for pkg in x: |
| if not mydbapi.match(pkg): |
| match=0 |
| break |
| if myportapi: |
| if not myportapi.match(pkg): |
| match=0 |
| break |
| if match: |
| writemsg("Installed match: %s\n" % (x), 2) |
| return x |
| |
| # Use binary packages if available. |
| if mybinapi: |
| for x in candidate: |
| match=1 |
| for pkg in x: |
| if not mybinapi.match(pkg): |
| match=0 |
| break |
| else: |
| writemsg("Binary match: %s\n" % (pkg), 2) |
| if match: |
| writemsg("Binary match final: %s\n" % (x), 2) |
| return x |
| |
| #use no-masked package(s) in portage tree |
| if myportapi: |
| for x in candidate: |
| match=1 |
| for pkg in x: |
| if not myportapi.match(pkg): |
| match=0 |
| break |
| if match: |
| writemsg("Porttree match: %s\n" % (x), 2) |
| return x |
| |
| #none of the no-masked pkg, use the first one |
| writemsg("Last resort candidate: %s\n" % (candidate[0]), 2) |
| return candidate[0] |
| else: |
| if dep_eval(reduced): |
| #deps satisfied, return empty list. |
| return [] |
| else: |
| returnme=[] |
| x=0 |
| while x<len(reduced): |
| if type(reduced[x])==types.ListType: |
| returnme+=dep_zapdeps(unreduced[x],reduced[x], vardbapi=vardbapi, use_binaries=use_binaries) |
| else: |
| if reduced[x]==False: |
| returnme.append(unreduced[x]) |
| x += 1 |
| return returnme |
| |
| def dep_getkey(mydep): |
| if not len(mydep): |
| return mydep |
| if mydep[0]=="*": |
| mydep=mydep[1:] |
| if mydep[-1]=="*": |
| mydep=mydep[:-1] |
| if mydep[0]=="!": |
| mydep=mydep[1:] |
| if mydep[:2] in [ ">=", "<=" ]: |
| mydep=mydep[2:] |
| elif mydep[:1] in "=<>~": |
| mydep=mydep[1:] |
| if isspecific(mydep): |
| mysplit=catpkgsplit(mydep) |
| if not mysplit: |
| return mydep |
| return mysplit[0]+"/"+mysplit[1] |
| else: |
| return mydep |
| |
| def dep_getcpv(mydep): |
| if not len(mydep): |
| return mydep |
| if mydep[0]=="*": |
| mydep=mydep[1:] |
| if mydep[-1]=="*": |
| mydep=mydep[:-1] |
| if mydep[0]=="!": |
| mydep=mydep[1:] |
| if mydep[:2] in [ ">=", "<=" ]: |
| mydep=mydep[2:] |
| elif mydep[:1] in "=<>~": |
| mydep=mydep[1:] |
| return mydep |
| |
| def cpv_getkey(mycpv): |
| myslash=mycpv.split("/") |
| mysplit=pkgsplit(myslash[-1]) |
| mylen=len(myslash) |
| if mylen==2: |
| return myslash[0]+"/"+mysplit[0] |
| elif mylen==1: |
| return mysplit[0] |
| else: |
| return mysplit |
| |
| def key_expand(mykey,mydb=None,use_cache=1): |
| mysplit=mykey.split("/") |
| if len(mysplit)==1: |
| if mydb and type(mydb)==types.InstanceType: |
| for x in settings.categories: |
| if mydb.cp_list(x+"/"+mykey,use_cache=use_cache): |
| return x+"/"+mykey |
| if virts_p.has_key(mykey): |
| return(virts_p[mykey][0]) |
| return "null/"+mykey |
| elif mydb: |
| if type(mydb)==types.InstanceType: |
| if (not mydb.cp_list(mykey,use_cache=use_cache)) and virts and virts.has_key(mykey): |
| return virts[mykey][0] |
| return mykey |
| |
| def cpv_expand(mycpv,mydb=None,use_cache=1): |
| """Given a string (packagename or virtual) expand it into a valid |
| cat/package string. Virtuals use the mydb to determine which provided |
| virtual is a valid choice and defaults to the first element when there |
| are no installed/available candidates.""" |
| myslash=mycpv.split("/") |
| mysplit=pkgsplit(myslash[-1]) |
| if len(myslash)>2: |
| # this is illegal case. |
| mysplit=[] |
| mykey=mycpv |
| elif len(myslash)==2: |
| if mysplit: |
| mykey=myslash[0]+"/"+mysplit[0] |
| else: |
| mykey=mycpv |
| if mydb: |
| writemsg("mydb.__class__: %s\n" % (mydb.__class__), 1) |
| if type(mydb)==types.InstanceType: |
| if (not mydb.cp_list(mykey,use_cache=use_cache)) and virts and virts.has_key(mykey): |
| writemsg("virts[%s]: %s\n" % (str(mykey),virts[mykey]), 1) |
| mykey_orig = mykey[:] |
| for vkey in virts[mykey]: |
| if mydb.cp_list(vkey,use_cache=use_cache): |
| mykey = vkey |
| writemsg("virts chosen: %s\n" % (mykey), 1) |
| break |
| if mykey == mykey_orig: |
| mykey=virts[mykey][0] |
| writemsg("virts defaulted: %s\n" % (mykey), 1) |
| #we only perform virtual expansion if we are passed a dbapi |
| else: |
| #specific cpv, no category, ie. "foo-1.0" |
| if mysplit: |
| myp=mysplit[0] |
| else: |
| # "foo" ? |
| myp=mycpv |
| mykey=None |
| matches=[] |
| if mydb: |
| for x in settings.categories: |
| if mydb.cp_list(x+"/"+myp,use_cache=use_cache): |
| matches.append(x+"/"+myp) |
| if (len(matches)>1): |
| raise ValueError, matches |
| elif matches: |
| mykey=matches[0] |
| |
| if not mykey and type(mydb)!=types.ListType: |
| if virts_p.has_key(myp): |
| mykey=virts_p[myp][0] |
| #again, we only perform virtual expansion if we have a dbapi (not a list) |
| if not mykey: |
| mykey="null/"+myp |
| if mysplit: |
| if mysplit[2]=="r0": |
| return mykey+"-"+mysplit[1] |
| else: |
| return mykey+"-"+mysplit[1]+"-"+mysplit[2] |
| else: |
| return mykey |
| |
| def dep_transform(mydep,oldkey,newkey): |
| origdep=mydep |
| if not len(mydep): |
| return mydep |
| if mydep[0]=="*": |
| mydep=mydep[1:] |
| prefix="" |
| postfix="" |
| if mydep[-1]=="*": |
| mydep=mydep[:-1] |
| postfix="*" |
| if mydep[:2] in [ ">=", "<=" ]: |
| prefix=mydep[:2] |
| mydep=mydep[2:] |
| elif mydep[:1] in "=<>~!": |
| prefix=mydep[:1] |
| mydep=mydep[1:] |
| if mydep==oldkey: |
| return prefix+newkey+postfix |
| else: |
| return origdep |
| |
| def dep_expand(mydep,mydb=None,use_cache=1): |
| if not len(mydep): |
| return mydep |
| if mydep[0]=="*": |
| mydep=mydep[1:] |
| prefix="" |
| postfix="" |
| if mydep[-1]=="*": |
| mydep=mydep[:-1] |
| postfix="*" |
| if mydep[:2] in [ ">=", "<=" ]: |
| prefix=mydep[:2] |
| mydep=mydep[2:] |
| elif mydep[:1] in "=<>~!": |
| prefix=mydep[:1] |
| mydep=mydep[1:] |
| return prefix+cpv_expand(mydep,mydb=mydb,use_cache=use_cache)+postfix |
| |
| def dep_check(depstring,mydbapi,mysettings,use="yes",mode=None,myuse=None,use_cache=1,use_binaries=0): |
| """Takes a depend string and parses the condition.""" |
| |
| #check_config_instance(mysettings) |
| |
| if use=="all": |
| #enable everything (for repoman) |
| myusesplit=["*"] |
| elif use=="yes": |
| if myuse==None: |
| #default behavior |
| myusesplit = string.split(mysettings["USE"]) |
| else: |
| myusesplit = myuse |
| # We've been given useflags to use. |
| #print "USE FLAGS PASSED IN." |
| #print myuse |
| #if "bindist" in myusesplit: |
| # print "BINDIST is set!" |
| #else: |
| # print "BINDIST NOT set." |
| else: |
| #we are being run by autouse(), don't consult USE vars yet. |
| # WE ALSO CANNOT USE SETTINGS |
| myusesplit=[] |
| |
| #convert parenthesis to sublists |
| mysplit = portage_dep.paren_reduce(depstring) |
| |
| if mysettings: |
| # XXX: use="all" is only used by repoman. Why would repoman checks want |
| # profile-masked USE flags to be enabled? |
| #if use=="all": |
| # mymasks=archlist[:] |
| #else: |
| mymasks=mysettings.usemask+archlist[:] |
| |
| while mysettings["ARCH"] in mymasks: |
| del mymasks[mymasks.index(mysettings["ARCH"])] |
| mysplit = portage_dep.use_reduce(mysplit,uselist=myusesplit,masklist=mymasks,matchall=(use=="all"),excludeall=[mysettings["ARCH"]]) |
| else: |
| mysplit = portage_dep.use_reduce(mysplit,uselist=myusesplit,matchall=(use=="all")) |
| |
| # Do the || conversions |
| mysplit=portage_dep.dep_opconvert(mysplit) |
| |
| #convert virtual dependencies to normal packages. |
| mysplit=dep_virtual(mysplit, mysettings) |
| #if mysplit==None, then we have a parse error (paren mismatch or misplaced ||) |
| #up until here, we haven't needed to look at the database tree |
| |
| if mysplit==None: |
| return [0,"Parse Error (parentheses mismatch?)"] |
| elif mysplit==[]: |
| #dependencies were reduced to nothing |
| return [1,[]] |
| mysplit2=mysplit[:] |
| mysplit2=dep_wordreduce(mysplit2,mysettings,mydbapi,mode,use_cache=use_cache) |
| if mysplit2==None: |
| return [0,"Invalid token"] |
| |
| writemsg("\n\n\n", 1) |
| writemsg("mysplit: %s\n" % (mysplit), 1) |
| writemsg("mysplit2: %s\n" % (mysplit2), 1) |
| myeval=dep_eval(mysplit2) |
| writemsg("myeval: %s\n" % (myeval), 1) |
| |
| if myeval: |
| return [1,[]] |
| else: |
| myzaps = dep_zapdeps(mysplit,mysplit2,vardbapi=mydbapi,use_binaries=use_binaries) |
| mylist = flatten(myzaps) |
| writemsg("myzaps: %s\n" % (myzaps), 1) |
| writemsg("mylist: %s\n" % (mylist), 1) |
| #remove duplicates |
| mydict={} |
| for x in mylist: |
| mydict[x]=1 |
| writemsg("mydict: %s\n" % (mydict), 1) |
| return [1,mydict.keys()] |
| |
| def dep_wordreduce(mydeplist,mysettings,mydbapi,mode,use_cache=1): |
| "Reduces the deplist to ones and zeros" |
| mypos=0 |
| deplist=mydeplist[:] |
| while mypos<len(deplist): |
| if type(deplist[mypos])==types.ListType: |
| #recurse |
| deplist[mypos]=dep_wordreduce(deplist[mypos],mysettings,mydbapi,mode,use_cache=use_cache) |
| elif deplist[mypos]=="||": |
| pass |
| else: |
| mykey = dep_getkey(deplist[mypos]) |
| if mysettings and mysettings.pprovideddict.has_key(mykey) and \ |
| match_from_list(deplist[mypos], mysettings.pprovideddict[mykey]): |
| deplist[mypos]=True |
| else: |
| if mode: |
| mydep=mydbapi.xmatch(mode,deplist[mypos]) |
| else: |
| mydep=mydbapi.match(deplist[mypos],use_cache=use_cache) |
| if mydep!=None: |
| tmp=(len(mydep)>=1) |
| if deplist[mypos][0]=="!": |
| #tmp=not tmp |
| # This is ad-hoc code. We should rewrite this later.. (See #52377) |
| # The reason is that portage uses fakedb when --update option now. |
| # So portage considers that a block package doesn't exist even if it exists. |
| # Then, #52377 happens. |
| # ==== start |
| # emerge checks if it's block or not, so we can always set tmp=False. |
| # but it's not clean.. |
| tmp=False |
| # ==== end |
| deplist[mypos]=tmp |
| else: |
| #encountered invalid string |
| return None |
| mypos=mypos+1 |
| return deplist |
| |
| def getmaskingreason(mycpv): |
| global portdb |
| mysplit = catpkgsplit(mycpv) |
| if not mysplit: |
| raise ValueError("invalid CPV: %s" % mycpv) |
| if not portdb.cpv_exists(mycpv): |
| raise KeyError("CPV %s does not exist" % mycpv) |
| mycp=mysplit[0]+"/"+mysplit[1] |
| |
| if settings.pmaskdict.has_key(mycp): |
| for x in settings.pmaskdict[mycp]: |
| if mycpv in portdb.xmatch("match-all", x): |
| pmaskfile = open(settings["PORTDIR"]+"/profiles/package.mask") |
| comment = "" |
| l = "\n" |
| while len(l) > 0: |
| l = pmaskfile.readline() |
| if len(l) == 0: |
| pmaskfile.close() |
| return None |
| if l[0] == "#": |
| comment += l |
| elif l == "\n": |
| comment = "" |
| elif l.strip() == x: |
| pmaskfile.close() |
| return comment |
| pmaskfile.close() |
| return None |
| |
| def getmaskingstatus(mycpv): |
| global portdb |
| mysplit = catpkgsplit(mycpv) |
| if not mysplit: |
| raise ValueError("invalid CPV: %s" % mycpv) |
| if not portdb.cpv_exists(mycpv): |
| raise KeyError("CPV %s does not exist" % mycpv) |
| mycp=mysplit[0]+"/"+mysplit[1] |
| |
| rValue = [] |
| |
| # profile checking |
| revmaskdict=settings.prevmaskdict |
| if revmaskdict.has_key(mycp): |
| for x in revmaskdict[mycp]: |
| if x[0]=="*": |
| myatom = x[1:] |
| else: |
| myatom = x |
| if not match_to_list(mycpv, [myatom]): |
| rValue.append("profile") |
| break |
| |
| # package.mask checking |
| maskdict=settings.pmaskdict |
| unmaskdict=settings.punmaskdict |
| if maskdict.has_key(mycp): |
| for x in maskdict[mycp]: |
| if mycpv in portdb.xmatch("match-all", x): |
| unmask=0 |
| if unmaskdict.has_key(mycp): |
| for z in unmaskdict[mycp]: |
| if mycpv in portdb.xmatch("match-all",z): |
| unmask=1 |
| break |
| if unmask==0: |
| rValue.append("package.mask") |
| |
| # keywords checking |
| mygroups, eapi = portdb.aux_get(mycpv, ["KEYWORDS", "EAPI"]) |
| if not eapi_is_supported(eapi): |
| return ["required EAPI %s, supported EAPI %s" % (eapi, portage_const.EAPI)] |
| mygroups = mygroups.split() |
| pgroups=groups[:] |
| myarch = settings["ARCH"] |
| pkgdict = settings.pkeywordsdict |
| |
| cp = dep_getkey(mycpv) |
| if pkgdict.has_key(cp): |
| matches = match_to_list(mycpv, pkgdict[cp].keys()) |
| for match in matches: |
| pgroups.extend(pkgdict[cp][match]) |
| |
| kmask = "missing" |
| |
| for keyword in pgroups: |
| if keyword in mygroups: |
| kmask=None |
| |
| if kmask: |
| fallback = None |
| for gp in mygroups: |
| if gp=="*": |
| kmask=None |
| break |
| elif gp=="-*": |
| fallback="-*" |
| elif gp=="-"+myarch: |
| kmask="-"+myarch |
| break |
| elif gp=="~"+myarch: |
| kmask="~"+myarch |
| break |
| if kmask == "missing" and fallback: |
| kmask = fallback |
| |
| if kmask: |
| rValue.append(kmask+" keyword") |
| return rValue |
| |
| def fixdbentries(old_value, new_value, dbdir): |
| """python replacement for the fixdbentries script, replaces old_value |
| with new_value for package names in files in dbdir.""" |
| for myfile in [f for f in os.listdir(dbdir) if not f == "CONTENTS"]: |
| f = open(dbdir+"/"+myfile, "r") |
| mycontent = f.read() |
| f.close() |
| if not mycontent.count(old_value): |
| continue |
| old_value = re.escape(old_value); |
| mycontent = re.sub(old_value+"$", new_value, mycontent) |
| mycontent = re.sub(old_value+"(\\s)", new_value+"\\1", mycontent) |
| mycontent = re.sub(old_value+"(-[^a-zA-Z])", new_value+"\\1", mycontent) |
| mycontent = re.sub(old_value+"([^a-zA-Z0-9-])", new_value+"\\1", mycontent) |
| f = open(dbdir+"/"+myfile, "w") |
| f.write(mycontent) |
| f.close() |
| |
| class packagetree: |
| def __init__(self,virtual,clone=None): |
| if clone: |
| self.tree=clone.tree.copy() |
| self.populated=clone.populated |
| self.virtual=clone.virtual |
| self.dbapi=None |
| else: |
| self.tree={} |
| self.populated=0 |
| self.virtual=virtual |
| self.dbapi=None |
| |
| def resolve_key(self,mykey): |
| return key_expand(mykey,mydb=self.dbapi) |
| |
| def dep_nomatch(self,mypkgdep): |
| mykey=dep_getkey(mypkgdep) |
| nolist=self.dbapi.cp_list(mykey) |
| mymatch=self.dbapi.match(mypkgdep) |
| if not mymatch: |
| return nolist |
| for x in mymatch: |
| if x in nolist: |
| nolist.remove(x) |
| return nolist |
| |
| def depcheck(self,mycheck,use="yes",myusesplit=None): |
| return dep_check(mycheck,self.dbapi,use=use,myuse=myusesplit) |
| |
| def populate(self): |
| "populates the tree with values" |
| populated=1 |
| pass |
| |
| def best(mymatches): |
| "accepts None arguments; assumes matches are valid." |
| global bestcount |
| if mymatches==None: |
| return "" |
| if not len(mymatches): |
| return "" |
| bestmatch=mymatches[0] |
| p2=catpkgsplit(bestmatch)[1:] |
| for x in mymatches[1:]: |
| p1=catpkgsplit(x)[1:] |
| if pkgcmp(p1,p2)>0: |
| bestmatch=x |
| p2=catpkgsplit(bestmatch)[1:] |
| return bestmatch |
| |
| def match_to_list(mypkg,mylist): |
| """(pkgname,list) |
| Searches list for entries that matches the package. |
| """ |
| matches=[] |
| for x in mylist: |
| if match_from_list(x,[mypkg]): |
| if x not in matches: |
| matches.append(x) |
| return matches |
| |
| def best_match_to_list(mypkg,mylist): |
| """(pkgname,list) |
| Returns the most specific entry (assumed to be the longest one) |
| that matches the package given. |
| """ |
| # XXX Assumption is wrong sometimes. |
| maxlen = 0 |
| bestm = None |
| for x in match_to_list(mypkg,mylist): |
| if len(x) > maxlen: |
| maxlen = len(x) |
| bestm = x |
| return bestm |
| |
| def catsplit(mydep): |
| return mydep.split("/", 1) |
| |
| def get_operator(mydep): |
| """ |
| returns '~', '=', '>', '<', '=*', '>=', or '<=' |
| """ |
| if mydep[0] == "~": |
| operator = "~" |
| elif mydep[0] == "=": |
| if mydep[-1] == "*": |
| operator = "=*" |
| else: |
| operator = "=" |
| elif mydep[0] in "><": |
| if len(mydep) > 1 and mydep[1] == "=": |
| operator = mydep[0:2] |
| else: |
| operator = mydep[0] |
| else: |
| operator = None |
| |
| return operator |
| |
| |
| def match_from_list(mydep,candidate_list): |
| if mydep[0] == "!": |
| mydep = mydep[1:] |
| |
| mycpv = dep_getcpv(mydep) |
| mycpv_cps = catpkgsplit(mycpv) # Can be None if not specific |
| |
| if not mycpv_cps: |
| cat,pkg = catsplit(mycpv) |
| ver = None |
| rev = None |
| else: |
| cat,pkg,ver,rev = mycpv_cps |
| if mydep == mycpv: |
| raise KeyError, "Specific key requires an operator (%s) (try adding an '=')" % (mydep) |
| |
| if ver and rev: |
| operator = get_operator(mydep) |
| if not operator: |
| writemsg("!!! Invalid atom: %s\n" % mydep) |
| return [] |
| else: |
| operator = None |
| |
| mylist = [] |
| |
| if operator == None: |
| for x in candidate_list: |
| xs = pkgsplit(x) |
| if xs == None: |
| if x != mycpv: |
| continue |
| elif xs[0] != mycpv: |
| continue |
| mylist.append(x) |
| |
| elif operator == "=": # Exact match |
| if mycpv in candidate_list: |
| mylist = [mycpv] |
| |
| elif operator == "=*": # glob match |
| # The old verion ignored _tag suffixes... This one doesn't. |
| for x in candidate_list: |
| if x[0:len(mycpv)] == mycpv: |
| mylist.append(x) |
| |
| elif operator == "~": # version, any revision, match |
| for x in candidate_list: |
| xs = catpkgsplit(x) |
| if xs[0:2] != mycpv_cps[0:2]: |
| continue |
| if xs[2] != ver: |
| continue |
| mylist.append(x) |
| |
| elif operator in [">", ">=", "<", "<="]: |
| for x in candidate_list: |
| try: |
| result = pkgcmp(pkgsplit(x), [cat+"/"+pkg,ver,rev]) |
| except SystemExit, e: |
| raise |
| except: |
| writemsg("\nInvalid package name: %s\n" % x) |
| sys.exit(73) |
| if result == None: |
| continue |
| elif operator == ">": |
| if result > 0: |
| mylist.append(x) |
| elif operator == ">=": |
| if result >= 0: |
| mylist.append(x) |
| elif operator == "<": |
| if result < 0: |
| mylist.append(x) |
| elif operator == "<=": |
| if result <= 0: |
| mylist.append(x) |
| else: |
| raise KeyError, "Unknown operator: %s" % mydep |
| else: |
| raise KeyError, "Unknown operator: %s" % mydep |
| |
| |
| return mylist |
| |
| |
| def match_from_list_original(mydep,mylist): |
| """(dep,list) |
| Reduces the list down to those that fit the dep |
| """ |
| mycpv=dep_getcpv(mydep) |
| if isspecific(mycpv): |
| cp_key=catpkgsplit(mycpv) |
| if cp_key==None: |
| return [] |
| else: |
| cp_key=None |
| #Otherwise, this is a special call; we can only select out of the ebuilds specified in the specified mylist |
| if (mydep[0]=="="): |
| if cp_key==None: |
| return [] |
| if mydep[-1]=="*": |
| #example: "=sys-apps/foo-1.0*" |
| try: |
| #now, we grab the version of our dependency... |
| mynewsplit=string.split(cp_key[2],'.') |
| #split it... |
| mynewsplit[-1]=`int(mynewsplit[-1])+1` |
| #and increment the last digit of the version by one. |
| #We don't need to worry about _pre and friends because they're not supported with '*' deps. |
| new_v=string.join(mynewsplit,".")+"_alpha0" |
| #new_v will be used later in the code when we do our comparisons using pkgcmp() |
| except SystemExit, e: |
| raise |
| except: |
| #erp, error. |
| return [] |
| mynodes=[] |
| cmp1=cp_key[1:] |
| cmp1[1]=cmp1[1]+"_alpha0" |
| cmp2=[cp_key[1],new_v,"r0"] |
| for x in mylist: |
| cp_x=catpkgsplit(x) |
| if cp_x==None: |
| #hrm, invalid entry. Continue. |
| continue |
| #skip entries in our list that do not have matching categories |
| if cp_key[0]!=cp_x[0]: |
| continue |
| # ok, categories match. Continue to next step. |
| if ((pkgcmp(cp_x[1:],cmp1)>=0) and (pkgcmp(cp_x[1:],cmp2)<0)): |
| # entry is >= the version in specified in our dependency, and <= the version in our dep + 1; add it: |
| mynodes.append(x) |
| return mynodes |
| else: |
| # Does our stripped key appear literally in our list? If so, we have a match; if not, we don't. |
| if mycpv in mylist: |
| return [mycpv] |
| else: |
| return [] |
| elif (mydep[0]==">") or (mydep[0]=="<"): |
| if cp_key==None: |
| return [] |
| if (len(mydep)>1) and (mydep[1]=="="): |
| cmpstr=mydep[0:2] |
| else: |
| cmpstr=mydep[0] |
| mynodes=[] |
| for x in mylist: |
| cp_x=catpkgsplit(x) |
| if cp_x==None: |
| #invalid entry; continue. |
| continue |
| if cp_key[0]!=cp_x[0]: |
| continue |
| if eval("pkgcmp(cp_x[1:],cp_key[1:])"+cmpstr+"0"): |
| mynodes.append(x) |
| return mynodes |
| elif mydep[0]=="~": |
| if cp_key==None: |
| return [] |
| myrev=-1 |
| for x in mylist: |
| cp_x=catpkgsplit(x) |
| if cp_x==None: |
| #invalid entry; continue |
| continue |
| if cp_key[0]!=cp_x[0]: |
| continue |
| if cp_key[2]!=cp_x[2]: |
| #if version doesn't match, skip it |
| continue |
| myint = int(cp_x[3][1:]) |
| if myint > myrev: |
| myrev = myint |
| mymatch = x |
| if myrev == -1: |
| return [] |
| else: |
| return [mymatch] |
| elif cp_key==None: |
| if mydep[0]=="!": |
| return [] |
| #we check ! deps in emerge itself, so always returning [] is correct. |
| mynodes=[] |
| cp_key=mycpv.split("/") |
| for x in mylist: |
| cp_x=catpkgsplit(x) |
| if cp_x==None: |
| #invalid entry; continue |
| continue |
| if cp_key[0]!=cp_x[0]: |
| continue |
| if cp_key[1]!=cp_x[1]: |
| continue |
| mynodes.append(x) |
| return mynodes |
| else: |
| return [] |
| |
| |
| class portagetree: |
| def __init__(self,root="/",virtual=None,clone=None): |
| global portdb |
| if clone: |
| self.root=clone.root |
| self.portroot=clone.portroot |
| self.pkglines=clone.pkglines |
| else: |
| self.root=root |
| self.portroot=settings["PORTDIR"] |
| self.virtual=virtual |
| self.dbapi=portdb |
| |
| def dep_bestmatch(self,mydep): |
| "compatibility method" |
| mymatch=self.dbapi.xmatch("bestmatch-visible",mydep) |
| if mymatch==None: |
| return "" |
| return mymatch |
| |
| def dep_match(self,mydep): |
| "compatibility method" |
| mymatch=self.dbapi.xmatch("match-visible",mydep) |
| if mymatch==None: |
| return [] |
| return mymatch |
| |
| def exists_specific(self,cpv): |
| return self.dbapi.cpv_exists(cpv) |
| |
| def getallnodes(self): |
| """new behavior: these are all *unmasked* nodes. There may or may not be available |
| masked package for nodes in this nodes list.""" |
| return self.dbapi.cp_all() |
| |
| def getname(self,pkgname): |
| "returns file location for this particular package (DEPRECATED)" |
| if not pkgname: |
| return "" |
| mysplit=string.split(pkgname,"/") |
| psplit=pkgsplit(mysplit[1]) |
| return self.portroot+"/"+mysplit[0]+"/"+psplit[0]+"/"+mysplit[1]+".ebuild" |
| |
| def resolve_specific(self,myspec): |
| cps=catpkgsplit(myspec) |
| if not cps: |
| return None |
| mykey=key_expand(cps[0]+"/"+cps[1],mydb=self.dbapi) |
| mykey=mykey+"-"+cps[2] |
| if cps[3]!="r0": |
| mykey=mykey+"-"+cps[3] |
| return mykey |
| |
| def depcheck(self,mycheck,use="yes",myusesplit=None): |
| return dep_check(mycheck,self.dbapi,use=use,myuse=myusesplit) |
| |
| def getslot(self,mycatpkg): |
| "Get a slot for a catpkg; assume it exists." |
| myslot = "" |
| try: |
| myslot=self.dbapi.aux_get(mycatpkg,["SLOT"])[0] |
| except SystemExit, e: |
| raise |
| except Exception, e: |
| pass |
| return myslot |
| |
| |
| class dbapi: |
| def __init__(self): |
| pass |
| |
| def close_caches(self): |
| pass |
| |
| def cp_list(self,cp,use_cache=1): |
| return |
| |
| def aux_get(self,mycpv,mylist): |
| "stub code for returning auxiliary db information, such as SLOT, DEPEND, etc." |
| 'input: "sys-apps/foo-1.0",["SLOT","DEPEND","HOMEPAGE"]' |
| 'return: ["0",">=sys-libs/bar-1.0","http://www.foo.com"] or [] if mycpv not found' |
| raise NotImplementedError |
| |
| def match(self,origdep,use_cache=1): |
| mydep=dep_expand(origdep,mydb=self) |
| mykey=dep_getkey(mydep) |
| mycat=mykey.split("/")[0] |
| return match_from_list(mydep,self.cp_list(mykey,use_cache=use_cache)) |
| |
| def match2(self,mydep,mykey,mylist): |
| writemsg("DEPRECATED: dbapi.match2\n") |
| match_from_list(mydep,mylist) |
| |
| def counter_tick(self,myroot,mycpv=None): |
| return self.counter_tick_core(myroot,incrementing=1,mycpv=mycpv) |
| |
| def get_counter_tick_core(self,myroot,mycpv=None): |
| return self.counter_tick_core(myroot,incrementing=0,mycpv=mycpv)+1 |
| |
| def counter_tick_core(self,myroot,incrementing=1,mycpv=None): |
| "This method will grab the next COUNTER value and record it back to the global file. Returns new counter value." |
| cpath=myroot+"var/cache/edb/counter" |
| changed=0 |
| min_counter = 0 |
| if mycpv: |
| mysplit = pkgsplit(mycpv) |
| for x in self.match(mysplit[0],use_cache=0): |
| # fixed bug #41062 |
| if x==mycpv: |
| continue |
| try: |
| old_counter = long(self.aux_get(x,["COUNTER"])[0]) |
| writemsg("COUNTER '%d' '%s'\n" % (old_counter, x),1) |
| except SystemExit, e: |
| raise |
| except: |
| old_counter = 0 |
| writemsg("!!! BAD COUNTER in '%s'\n" % (x)) |
| if old_counter > min_counter: |
| min_counter = old_counter |
| |
| # We write our new counter value to a new file that gets moved into |
| # place to avoid filesystem corruption. |
| if os.path.exists(cpath): |
| cfile=open(cpath, "r") |
| try: |
| counter=long(cfile.readline()) |
| except (ValueError,OverflowError): |
| try: |
| counter=long(commands.getoutput("for FILE in $(find /"+VDB_PATH+" -type f -name COUNTER); do echo $(<${FILE}); done | sort -n | tail -n1 | tr -d '\n'")) |
| writemsg("!!! COUNTER was corrupted; resetting to value of %d\n" % counter) |
| changed=1 |
| except (ValueError,OverflowError): |
| writemsg("!!! COUNTER data is corrupt in pkg db. The values need to be\n") |
| writemsg("!!! corrected/normalized so that portage can operate properly.\n") |
| writemsg("!!! A simple solution is not yet available so try #gentoo on IRC.\n") |
| sys.exit(2) |
| cfile.close() |
| else: |
| try: |
| counter=long(commands.getoutput("for FILE in $(find /"+VDB_PATH+" -type f -name COUNTER); do echo $(<${FILE}); done | sort -n | tail -n1 | tr -d '\n'")) |
| writemsg("!!! Global counter missing. Regenerated from counter files to: %s\n" % counter) |
| except SystemExit, e: |
| raise |
| except: |
| writemsg("!!! Initializing global counter.\n") |
| counter=long(0) |
| changed=1 |
| |
| if counter < min_counter: |
| counter = min_counter+1000 |
| changed = 1 |
| |
| if incrementing or changed: |
| |
| #increment counter |
| counter += 1 |
| # update new global counter file |
| newcpath=cpath+".new" |
| newcfile=open(newcpath,"w") |
| newcfile.write(str(counter)) |
| newcfile.close() |
| # now move global counter file into place |
| os.rename(newcpath,cpath) |
| return counter |
| |
| def invalidentry(self, mypath): |
| if re.search("portage_lockfile$",mypath): |
| if not os.environ.has_key("PORTAGE_MASTER_PID"): |
| writemsg("Lockfile removed: %s\n" % mypath, 1) |
| portage_locks.unlockfile((mypath,None,None)) |
| else: |
| # Nothing we can do about it. We're probably sandboxed. |
| pass |
| elif re.search(".*/-MERGING-(.*)",mypath): |
| if os.path.exists(mypath): |
| writemsg(red("INCOMPLETE MERGE:")+" "+mypath+"\n") |
| else: |
| writemsg("!!! Invalid db entry: %s\n" % mypath) |
| |
| |
| |
| class fakedbapi(dbapi): |
| "This is a dbapi to use for the emptytree function. It's empty, but things can be added to it." |
| def __init__(self): |
| self.cpvdict={} |
| self.cpdict={} |
| |
| def cpv_exists(self,mycpv): |
| return self.cpvdict.has_key(mycpv) |
| |
| def cp_list(self,mycp,use_cache=1): |
| if not self.cpdict.has_key(mycp): |
| return [] |
| else: |
| return self.cpdict[mycp] |
| |
| def cp_all(self): |
| returnme=[] |
| for x in self.cpdict.keys(): |
| returnme.extend(self.cpdict[x]) |
| return returnme |
| |
| def cpv_inject(self,mycpv): |
| """Adds a cpv from the list of available packages.""" |
| mycp=cpv_getkey(mycpv) |
| self.cpvdict[mycpv]=1 |
| if not self.cpdict.has_key(mycp): |
| self.cpdict[mycp]=[] |
| if not mycpv in self.cpdict[mycp]: |
| self.cpdict[mycp].append(mycpv) |
| |
| #def cpv_virtual(self,oldcpv,newcpv): |
| # """Maps a cpv to the list of available packages.""" |
| # mycp=cpv_getkey(newcpv) |
| # self.cpvdict[newcpv]=1 |
| # if not self.virtdict.has_key(mycp): |
| # self.virtdict[mycp]=[] |
| # if not mycpv in self.virtdict[mycp]: |
| # self.virtdict[mycp].append(oldcpv) |
| # cpv_remove(oldcpv) |
| |
| def cpv_remove(self,mycpv): |
| """Removes a cpv from the list of available packages.""" |
| mycp=cpv_getkey(mycpv) |
| if self.cpvdict.has_key(mycpv): |
| del self.cpvdict[mycpv] |
| if not self.cpdict.has_key(mycp): |
| return |
| while mycpv in self.cpdict[mycp]: |
| del self.cpdict[mycp][self.cpdict[mycp].index(mycpv)] |
| if not len(self.cpdict[mycp]): |
| del self.cpdict[mycp] |
| |
| class bindbapi(fakedbapi): |
| def __init__(self,mybintree=None): |
| self.bintree = mybintree |
| self.cpvdict={} |
| self.cpdict={} |
| |
| def aux_get(self,mycpv,wants): |
| mysplit = string.split(mycpv,"/") |
| mylist = [] |
| tbz2name = mysplit[1]+".tbz2" |
| if self.bintree and not self.bintree.isremote(mycpv): |
| tbz2 = xpak.tbz2(self.bintree.getname(mycpv)) |
| for x in wants: |
| if self.bintree and self.bintree.isremote(mycpv): |
| # We use the cache for remote packages |
| if self.bintree.remotepkgs[tbz2name].has_key(x): |
| mylist.append(self.bintree.remotepkgs[tbz2name][x][:]) # [:] Copy String |
| else: |
| mylist.append("") |
| else: |
| myval = tbz2.getfile(x) |
| if myval == None: |
| myval = "" |
| else: |
| myval = string.join(myval.split(),' ') |
| mylist.append(myval) |
| if "EAPI" in wants: |
| idx = wants.index("EAPI") |
| if not mylist[idx]: |
| mylist[idx] = "0" |
| return mylist |
| |
| |
| cptot=0 |
| class vardbapi(dbapi): |
| def __init__(self,root,categories=None): |
| self.root = root[:] |
| #cache for category directory mtimes |
| self.mtdircache = {} |
| #cache for dependency checks |
| self.matchcache = {} |
| #cache for cp_list results |
| self.cpcache = {} |
| self.blockers = None |
| self.categories = copy.deepcopy(categories) |
| |
| def cpv_exists(self,mykey): |
| "Tells us whether an actual ebuild exists on disk (no masking)" |
| return os.path.exists(self.root+VDB_PATH+"/"+mykey) |
| |
| def cpv_counter(self,mycpv): |
| "This method will grab the COUNTER. Returns a counter value." |
| cdir=self.root+VDB_PATH+"/"+mycpv |
| cpath=self.root+VDB_PATH+"/"+mycpv+"/COUNTER" |
| |
| # We write our new counter value to a new file that gets moved into |
| # place to avoid filesystem corruption on XFS (unexpected reboot.) |
| corrupted=0 |
| if os.path.exists(cpath): |
| cfile=open(cpath, "r") |
| try: |
| counter=long(cfile.readline()) |
| except ValueError: |
| print "portage: COUNTER for",mycpv,"was corrupted; resetting to value of 0" |
| counter=long(0) |
| corrupted=1 |
| cfile.close() |
| elif os.path.exists(cdir): |
| mys = pkgsplit(mycpv) |
| myl = self.match(mys[0],use_cache=0) |
| print mys,myl |
| if len(myl) == 1: |
| try: |
| # Only one package... Counter doesn't matter. |
| myf = open(cpath, "w") |
| myf.write("1") |
| myf.flush() |
| myf.close() |
| counter = 1 |
| except SystemExit, e: |
| raise |
| except Exception, e: |
| writemsg("!!! COUNTER file is missing for "+str(mycpv)+" in /var/db.\n") |
| writemsg("!!! Please run /usr/lib/portage/bin/fix-db.pl or\n") |
| writemsg("!!! Please run /usr/lib/portage/bin/fix-db.py or\n") |
| writemsg("!!! unmerge this exact version.\n") |
| writemsg("!!! %s\n" % e) |
| sys.exit(1) |
| else: |
| writemsg("!!! COUNTER file is missing for "+str(mycpv)+" in /var/db.\n") |
| writemsg("!!! Please run /usr/lib/portage/bin/fix-db.pl or\n") |
| writemsg("!!! Please run /usr/lib/portage/bin/fix-db.py or\n") |
| writemsg("!!! remerge the package.\n") |
| sys.exit(1) |
| else: |
| counter=long(0) |
| if corrupted: |
| newcpath=cpath+".new" |
| # update new global counter file |
| newcfile=open(newcpath,"w") |
| newcfile.write(str(counter)) |
| newcfile.close() |
| # now move global counter file into place |
| os.rename(newcpath,cpath) |
| return counter |
| |
| def cpv_inject(self,mycpv): |
| "injects a real package into our on-disk database; assumes mycpv is valid and doesn't already exist" |
| os.makedirs(self.root+VDB_PATH+"/"+mycpv) |
| counter=db[self.root]["vartree"].dbapi.counter_tick(self.root,mycpv=mycpv) |
| # write local package counter so that emerge clean does the right thing |
| lcfile=open(self.root+VDB_PATH+"/"+mycpv+"/COUNTER","w") |
| lcfile.write(str(counter)) |
| lcfile.close() |
| |
| def isInjected(self,mycpv): |
| if self.cpv_exists(mycpv): |
| if os.path.exists(self.root+VDB_PATH+"/"+mycpv+"/INJECTED"): |
| return True |
| if not os.path.exists(self.root+VDB_PATH+"/"+mycpv+"/CONTENTS"): |
| return True |
| return False |
| |
| def move_ent(self,mylist): |
| origcp=mylist[1] |
| newcp=mylist[2] |
| origmatches=self.match(origcp,use_cache=0) |
| if not origmatches: |
| return |
| for mycpv in origmatches: |
| mycpsplit=catpkgsplit(mycpv) |
| mynewcpv=newcp+"-"+mycpsplit[2] |
| mynewcat=newcp.split("/")[0] |
| if mycpsplit[3]!="r0": |
| mynewcpv += "-"+mycpsplit[3] |
| mycpsplit_new = catpkgsplit(mynewcpv) |
| origpath=self.root+VDB_PATH+"/"+mycpv |
| if not os.path.exists(origpath): |
| continue |
| writemsg("@") |
| if not os.path.exists(self.root+VDB_PATH+"/"+mynewcat): |
| #create the directory |
| os.makedirs(self.root+VDB_PATH+"/"+mynewcat) |
| newpath=self.root+VDB_PATH+"/"+mynewcpv |
| if os.path.exists(newpath): |
| #dest already exists; keep this puppy where it is. |
| continue |
| spawn(MOVE_BINARY+" "+origpath+" "+newpath,settings, free=1) |
| |
| # We need to rename the ebuild now. |
| old_eb_path = newpath+"/"+mycpsplit[1] +"-"+mycpsplit[2] |
| new_eb_path = newpath+"/"+mycpsplit_new[1]+"-"+mycpsplit[2] |
| if mycpsplit[3] != "r0": |
| old_eb_path += "-"+mycpsplit[3] |
| new_eb_path += "-"+mycpsplit[3] |
| if os.path.exists(old_eb_path+".ebuild"): |
| os.rename(old_eb_path+".ebuild", new_eb_path+".ebuild") |
| |
| catfile=open(newpath+"/CATEGORY", "w") |
| catfile.write(mynewcat+"\n") |
| catfile.close() |
| |
| dbdir = self.root+VDB_PATH |
| for catdir in listdir(dbdir): |
| catdir = dbdir+"/"+catdir |
| if os.path.isdir(catdir): |
| for pkgdir in listdir(catdir): |
| pkgdir = catdir+"/"+pkgdir |
| if os.path.isdir(pkgdir): |
| fixdbentries(origcp, newcp, pkgdir) |
| |
| def move_slot_ent(self,mylist): |
| pkg=mylist[1] |
| origslot=mylist[2] |
| newslot=mylist[3] |
| |
| origmatches=self.match(pkg,use_cache=0) |
| if not origmatches: |
| return |
| for mycpv in origmatches: |
| origpath=self.root+VDB_PATH+"/"+mycpv |
| if not os.path.exists(origpath): |
| continue |
| |
| slot=grabfile(origpath+"/SLOT"); |
| if (not slot): |
| continue |
| |
| if (slot[0]!=origslot): |
| continue |
| |
| writemsg("s") |
| slotfile=open(origpath+"/SLOT", "w") |
| slotfile.write(newslot+"\n") |
| slotfile.close() |
| |
| def cp_list(self,mycp,use_cache=1): |
| mysplit=mycp.split("/") |
| if mysplit[0] == '*': |
| mysplit[0] = mysplit[0][1:] |
| try: |
| mystat=os.stat(self.root+VDB_PATH+"/"+mysplit[0])[stat.ST_MTIME] |
| except OSError: |
| mystat=0 |
| if use_cache and self.cpcache.has_key(mycp): |
| cpc=self.cpcache[mycp] |
| if cpc[0]==mystat: |
| return cpc[1] |
| list=listdir(self.root+VDB_PATH+"/"+mysplit[0],EmptyOnError=1) |
| |
| if (list==None): |
| return [] |
| returnme=[] |
| for x in list: |
| if x[0] == '-': |
| #writemsg(red("INCOMPLETE MERGE:")+str(x[len("-MERGING-"):])+"\n") |
| continue |
| ps=pkgsplit(x) |
| if not ps: |
| self.invalidentry(self.root+VDB_PATH+"/"+mysplit[0]+"/"+x) |
| continue |
| if len(mysplit) > 1: |
| if ps[0]==mysplit[1]: |
| returnme.append(mysplit[0]+"/"+x) |
| if use_cache: |
| self.cpcache[mycp]=[mystat,returnme] |
| elif self.cpcache.has_key(mycp): |
| del self.cpcache[mycp] |
| return returnme |
| |
| def cpv_all(self,use_cache=1): |
| returnme=[] |
| basepath = self.root+VDB_PATH+"/" |
| |
| mycats = self.categories |
| if mycats == None: |
| # XXX: CIRCULAR DEP! This helps backwards compat. --NJ (10 Sept 2004) |
| mycats = settings.categories |
| |
| for x in mycats: |
| for y in listdir(basepath+x,EmptyOnError=1): |
| subpath = x+"/"+y |
| # -MERGING- should never be a cpv, nor should files. |
| if os.path.isdir(basepath+subpath) and (pkgsplit(y) is not None): |
| returnme += [subpath] |
| return returnme |
| |
| def cp_all(self,use_cache=1): |
| mylist = self.cpv_all(use_cache=use_cache) |
| d={} |
| for y in mylist: |
| if y[0] == '*': |
| y = y[1:] |
| mysplit=catpkgsplit(y) |
| if not mysplit: |
| self.invalidentry(self.root+VDB_PATH+"/"+y) |
| continue |
| d[mysplit[0]+"/"+mysplit[1]] = None |
| return d.keys() |
| |
| def checkblockers(self,origdep): |
| pass |
| |
| def match(self,origdep,use_cache=1): |
| "caching match function" |
| mydep=dep_expand(origdep,mydb=self,use_cache=use_cache) |
| mykey=dep_getkey(mydep) |
| mycat=mykey.split("/")[0] |
| if not use_cache: |
| if self.matchcache.has_key(mycat): |
| del self.mtdircache[mycat] |
| del self.matchcache[mycat] |
| return match_from_list(mydep,self.cp_list(mykey,use_cache=use_cache)) |
| try: |
| curmtime=os.stat(self.root+VDB_PATH+"/"+mycat)[stat.ST_MTIME] |
| except SystemExit, e: |
| raise |
| except: |
| curmtime=0 |
| |
| if not self.matchcache.has_key(mycat) or not self.mtdircache[mycat]==curmtime: |
| # clear cache entry |
| self.mtdircache[mycat]=curmtime |
| self.matchcache[mycat]={} |
| if not self.matchcache[mycat].has_key(mydep): |
| mymatch=match_from_list(mydep,self.cp_list(mykey,use_cache=use_cache)) |
| self.matchcache[mycat][mydep]=mymatch |
| return self.matchcache[mycat][mydep][:] |
| |
| def findname(self, mycpv): |
| return self.root+VDB_PATH+"/"+str(mycpv)+"/"+mycpv.split("/")[1]+".ebuild" |
| |
| def aux_get(self, mycpv, wants): |
| global auxdbkeys |
| results = [] |
| for x in wants: |
| myfn = self.root+VDB_PATH+"/"+str(mycpv)+"/"+str(x) |
| if os.access(myfn,os.R_OK): |
| myf = open(myfn, "r") |
| myd = myf.read() |
| myf.close() |
| myd = re.sub("[\n\r\t]+"," ",myd) |
| myd = re.sub(" +"," ",myd) |
| myd = string.strip(myd) |
| else: |
| myd = "" |
| results.append(myd) |
| if "EAPI" in wants: |
| idx = wants.index("EAPI") |
| if not results[idx]: |
| results[idx] = "0" |
| return results |
| |
| |
| class vartree(packagetree): |
| "this tree will scan a var/db/pkg database located at root (passed to init)" |
| def __init__(self,root="/",virtual=None,clone=None,categories=None): |
| if clone: |
| self.root = clone.root[:] |
| self.dbapi = copy.deepcopy(clone.dbapi) |
| self.populated = 1 |
| else: |
| self.root = root[:] |
| self.dbapi = vardbapi(self.root,categories=categories) |
| self.populated = 1 |
| |
| def zap(self,mycpv): |
| return |
| |
| def inject(self,mycpv): |
| return |
| |
| def get_provide(self,mycpv): |
| myprovides=[] |
| try: |
| mylines = grabfile(self.root+VDB_PATH+"/"+mycpv+"/PROVIDE") |
| if mylines: |
| myuse = grabfile(self.root+VDB_PATH+"/"+mycpv+"/USE") |
| myuse = string.split(string.join(myuse)) |
| mylines = string.join(mylines) |
| mylines = flatten(portage_dep.use_reduce(portage_dep.paren_reduce(mylines), uselist=myuse)) |
| for myprovide in mylines: |
| mys = catpkgsplit(myprovide) |
| if not mys: |
| mys = string.split(myprovide, "/") |
| myprovides += [mys[0] + "/" + mys[1]] |
| return myprovides |
| except SystemExit, e: |
| raise |
| except Exception, e: |
| print |
| print "Check " + self.root+VDB_PATH+"/"+mycpv+"/PROVIDE and USE." |
| print "Possibly Invalid: " + str(mylines) |
| print "Exception: "+str(e) |
| print |
| return [] |
| |
| def get_all_provides(self): |
| myprovides = {} |
| for node in self.getallcpv(): |
| for mykey in self.get_provide(node): |
| if myprovides.has_key(mykey): |
| myprovides[mykey] += [node] |
| else: |
| myprovides[mykey] = [node] |
| return myprovides |
| |
| def dep_bestmatch(self,mydep,use_cache=1): |
| "compatibility method -- all matches, not just visible ones" |
| #mymatch=best(match(dep_expand(mydep,self.dbapi),self.dbapi)) |
| mymatch=best(self.dbapi.match(dep_expand(mydep,mydb=self.dbapi),use_cache=use_cache)) |
| if mymatch==None: |
| return "" |
| else: |
| return mymatch |
| |
| def dep_match(self,mydep,use_cache=1): |
| "compatibility method -- we want to see all matches, not just visible ones" |
| #mymatch=match(mydep,self.dbapi) |
| mymatch=self.dbapi.match(mydep,use_cache=use_cache) |
| if mymatch==None: |
| return [] |
| else: |
| return mymatch |
| |
| def exists_specific(self,cpv): |
| return self.dbapi.cpv_exists(cpv) |
| |
| def getallcpv(self): |
| """temporary function, probably to be renamed --- Gets a list of all |
| category/package-versions installed on the system.""" |
| return self.dbapi.cpv_all() |
| |
| def getallnodes(self): |
| """new behavior: these are all *unmasked* nodes. There may or may not be available |
| masked package for nodes in this nodes list.""" |
| return self.dbapi.cp_all() |
| |
| def exists_specific_cat(self,cpv,use_cache=1): |
| cpv=key_expand(cpv,mydb=self.dbapi,use_cache=use_cache) |
| a=catpkgsplit(cpv) |
| if not a: |
| return 0 |
| mylist=listdir(self.root+VDB_PATH+"/"+a[0],EmptyOnError=1) |
| for x in mylist: |
| b=pkgsplit(x) |
| if not b: |
| self.dbapi.invalidentry(self.root+VDB_PATH+"/"+a[0]+"/"+x) |
| continue |
| if a[1]==b[0]: |
| return 1 |
| return 0 |
| |
| def getebuildpath(self,fullpackage): |
| cat,package=fullpackage.split("/") |
| return self.root+VDB_PATH+"/"+fullpackage+"/"+package+".ebuild" |
| |
| def getnode(self,mykey,use_cache=1): |
| mykey=key_expand(mykey,mydb=self.dbapi,use_cache=use_cache) |
| if not mykey: |
| return [] |
| mysplit=mykey.split("/") |
| mydirlist=listdir(self.root+VDB_PATH+"/"+mysplit[0],EmptyOnError=1) |
| returnme=[] |
| for x in mydirlist: |
| mypsplit=pkgsplit(x) |
| if not mypsplit: |
| self.dbapi.invalidentry(self.root+VDB_PATH+"/"+mysplit[0]+"/"+x) |
| continue |
| if mypsplit[0]==mysplit[1]: |
| appendme=[mysplit[0]+"/"+x,[mysplit[0],mypsplit[0],mypsplit[1],mypsplit[2]]] |
| returnme.append(appendme) |
| return returnme |
| |
| |
| def getslot(self,mycatpkg): |
| "Get a slot for a catpkg; assume it exists." |
| myslot = "" |
| try: |
| myslot=string.join(grabfile(self.root+VDB_PATH+"/"+mycatpkg+"/SLOT")) |
| except SystemExit, e: |
| raise |
| except Exception, e: |
| pass |
| return myslot |
| |
| def hasnode(self,mykey,use_cache): |
| """Does the particular node (cat/pkg key) exist?""" |
| mykey=key_expand(mykey,mydb=self.dbapi,use_cache=use_cache) |
| mysplit=mykey.split("/") |
| mydirlist=listdir(self.root+VDB_PATH+"/"+mysplit[0],EmptyOnError=1) |
| for x in mydirlist: |
| mypsplit=pkgsplit(x) |
| if not mypsplit: |
| self.dbapi.invalidentry(self.root+VDB_PATH+"/"+mysplit[0]+"/"+x) |
| continue |
| if mypsplit[0]==mysplit[1]: |
| return 1 |
| return 0 |
| |
| def populate(self): |
| self.populated=1 |
| |
| # ---------------------------------------------------------------------------- |
| class eclass_cache: |
| """Maintains the cache information about eclasses used in ebuild.""" |
| def __init__(self,porttree_root,settings): |
| self.porttree_root = porttree_root |
| self.settings = settings |
| self.depcachedir = self.settings.depcachedir[:] |
| |
| self.dbmodule = self.settings.load_best_module("eclass_cache.dbmodule") |
| |
| self.packages = {} # {"PV": {"eclass1": ["location", "_mtime_"]}} |
| self.eclasses = {} # {"Name": ["location","_mtime_"]} |
| |
| # don't fool with porttree ordering unless you *ensure* that ebuild.sh's inherit |
| # ordering is *exactly* the same |
| self.porttrees=[self.porttree_root] |
| self.porttrees.extend(self.settings["PORTDIR_OVERLAY"].split()) |
| #normalize the path now, so it's not required later. |
| self.porttrees = [os.path.normpath(x) for x in self.porttrees] |
| self.update_eclasses() |
| |
| def close_caches(self): |
| for x in self.packages.keys(): |
| for y in self.packages[x].keys(): |
| try: |
| self.packages[x][y].sync() |
| self.packages[x][y].close() |
| except SystemExit, e: |
| raise |
| except Exception,e: |
| writemsg("Exception when closing DB: %s: %s\n" % (Exception,e)) |
| del self.packages[x][y] |
| del self.packages[x] |
| |
| def flush_cache(self): |
| self.packages = {} |
| self.eclasses = {} |
| self.update_eclasses() |
| |
| def update_eclasses(self): |
| self.eclasses = {} |
| for x in suffix_array(self.porttrees, "/eclass"): |
| if x and os.path.exists(x): |
| dirlist = listdir(x) |
| for y in dirlist: |
| if y[-len(".eclass"):]==".eclass": |
| try: |
| ys=y[:-len(".eclass")] |
| ymtime=os.stat(x+"/"+y)[stat.ST_MTIME] |
| except SystemExit, e: |
| raise |
| except: |
| continue |
| self.eclasses[ys] = [x, ymtime] |
| |
| def setup_package(self, location, cat, pkg): |
| if not self.packages.has_key(location): |
| self.packages[location] = {} |
| |
| if not self.packages[location].has_key(cat): |
| try: |
| self.packages[location][cat] = self.dbmodule(self.depcachedir+"/"+location, cat+"-eclass", [], uid, portage_gid) |
| except SystemExit, e: |
| raise |
| except Exception, e: |
| writemsg("\n!!! Failed to open the dbmodule for eclass caching.\n") |
| writemsg("!!! Generally these are permission problems. Caught exception follows:\n") |
| writemsg("!!! "+str(e)+"\n") |
| writemsg("!!! Dirname: "+str(self.depcachedir+"/"+location)+"\n") |
| writemsg("!!! Basename: "+str(cat+"-eclass")+"\n\n") |
| sys.exit(123) |
| |
| def sync(self, location, cat, pkg): |
| if self.packages[location].has_key(cat): |
| self.packages[location][cat].sync() |
| |
| def update_package(self, location, cat, pkg, eclass_list): |
| self.setup_package(location, cat, pkg) |
| if not eclass_list: |
| return 1 |
| |
| data = {} |
| for x in eclass_list: |
| if x not in self.eclasses: |
| writemsg("Eclass '%s' does not exist for '%s'\n" % (x, cat+"/"+pkg)) |
| return 0 |
| data[x] = [self.eclasses[x][0],self.eclasses[x][1]] |
| |
| self.packages[location][cat][pkg] = data |
| self.sync(location,cat,pkg) |
| return 1 |
| |
| def is_current(self, location, cat, pkg, eclass_list): |
| self.setup_package(location, cat, pkg) |
| |
| if not eclass_list: |
| return 1 |
| |
| if not (self.packages[location][cat].has_key(pkg) and self.packages[location][cat][pkg] and eclass_list): |
| return 0 |
| |
| myp = self.packages[location][cat][pkg] |
| for x in eclass_list: |
| if not (x in self.eclasses and x in myp and myp[x] == self.eclasses[x]): |
| return 0 |
| |
| return 1 |
| |
| # ---------------------------------------------------------------------------- |
| |
| auxdbkeys=[ |
| 'DEPEND', 'RDEPEND', 'SLOT', 'SRC_URI', |
| 'RESTRICT', 'HOMEPAGE', 'LICENSE', 'DESCRIPTION', |
| 'KEYWORDS', 'INHERITED', 'IUSE', 'CDEPEND', |
| 'PDEPEND', 'PROVIDE', 'EAPI', |
| 'UNUSED_01', 'UNUSED_02', 'UNUSED_03', 'UNUSED_04', |
| 'UNUSED_05', 'UNUSED_06', 'UNUSED_07', |
| ] |
| auxdbkeylen=len(auxdbkeys) |
| |
| def close_portdbapi_caches(): |
| for i in portdbapi.portdbapi_instances: |
| i.close_caches() |
| class portdbapi(dbapi): |
| """this tree will scan a portage directory located at root (passed to init)""" |
| portdbapi_instances = [] |
| |
| def __init__(self,porttree_root,mysettings=None): |
| portdbapi.portdbapi_instances.append(self) |
| self.lock_held = 0; |
| |
| if mysettings: |
| self.mysettings = mysettings |
| else: |
| self.mysettings = config(clone=settings) |
| |
| self.manifestVerifyLevel = None |
| self.manifestVerifier = None |
| self.manifestCache = {} # {location: [stat, md5]} |
| self.manifestMissingCache = [] |
| |
| if "gpg" in self.mysettings.features: |
| self.manifestVerifyLevel = portage_gpg.EXISTS |
| if "strict" in self.mysettings.features: |
| self.manifestVerifyLevel = portage_gpg.MARGINAL |
| self.manifestVerifier = portage_gpg.FileChecker(self.mysettings["PORTAGE_GPG_DIR"], "gentoo.gpg", minimumTrust=self.manifestVerifyLevel) |
| elif "severe" in self.mysettings.features: |
| self.manifestVerifyLevel = portage_gpg.TRUSTED |
| self.manifestVerifier = portage_gpg.FileChecker(self.mysettings["PORTAGE_GPG_DIR"], "gentoo.gpg", requireSignedRing=True, minimumTrust=self.manifestVerifyLevel) |
| else: |
| self.manifestVerifier = portage_gpg.FileChecker(self.mysettings["PORTAGE_GPG_DIR"], "gentoo.gpg", minimumTrust=self.manifestVerifyLevel) |
| |
| #self.root=settings["PORTDIR"] |
| self.porttree_root = porttree_root |
| |
| self.depcachedir = self.mysettings.depcachedir[:] |
| |
| self.tmpfs = self.mysettings["PORTAGE_TMPFS"] |
| if self.tmpfs and not os.path.exists(self.tmpfs): |
| self.tmpfs = None |
| if self.tmpfs and not os.access(self.tmpfs, os.W_OK): |
| self.tmpfs = None |
| if self.tmpfs and not os.access(self.tmpfs, os.R_OK): |
| self.tmpfs = None |
| |
| self.eclassdb = eclass_cache(self.porttree_root, self.mysettings) |
| |
| self.metadb = {} |
| self.metadbmodule = self.mysettings.load_best_module("portdbapi.metadbmodule") |
| |
| self.auxdb = {} |
| self.auxdbmodule = self.mysettings.load_best_module("portdbapi.auxdbmodule") |
| |
| #if the portdbapi is "frozen", then we assume that we can cache everything (that no updates to it are happening) |
| self.xcache={} |
| self.frozen=0 |
| |
| self.porttrees=[self.porttree_root]+self.mysettings["PORTDIR_OVERLAY"].split() |
| |
| def close_caches(self): |
| for x in self.auxdb.keys(): |
| for y in self.auxdb[x].keys(): |
| self.auxdb[x][y].sync() |
| self.auxdb[x][y].close() |
| del self.auxdb[x][y] |
| del self.auxdb[x] |
| self.eclassdb.close_caches() |
| |
| def flush_cache(self): |
| self.metadb = {} |
| self.auxdb = {} |
| self.eclassdb.flush_cache() |
| |
| def finddigest(self,mycpv): |
| try: |
| mydig = self.findname2(mycpv)[0] |
| mydigs = string.split(mydig, "/")[:-1] |
| mydig = string.join(mydigs, "/") |
| |
| mysplit = mycpv.split("/") |
| except SystemExit, e: |
| raise |
| except: |
| return "" |
| return mydig+"/files/digest-"+mysplit[-1] |
| |
| def findname(self,mycpv): |
| return self.findname2(mycpv)[0] |
| |
| def findname2(self,mycpv): |
| "returns file location for this particular package and in_overlay flag" |
| if not mycpv: |
| return "",0 |
| mysplit=mycpv.split("/") |
| |
| psplit=pkgsplit(mysplit[1]) |
| ret=None |
| if psplit: |
| for x in self.porttrees: |
| # XXX Why are there errors here? XXX |
| try: |
| file=x+"/"+mysplit[0]+"/"+psplit[0]+"/"+mysplit[1]+".ebuild" |
| except SystemExit, e: |
| raise |
| except Exception, e: |
| print |
| print "!!! Problem with determining the name/location of an ebuild." |
| print "!!! Please report this on IRC and bugs if you are not causing it." |
| print "!!! mycpv: ",mycpv |
| print "!!! mysplit:",mysplit |
| print "!!! psplit: ",psplit |
| print "!!! error: ",e |
| print |
| sys.exit(17) |
| |
| if os.access(file, os.R_OK): |
| # when found |
| ret=[file, x] |
| if ret: |
| return ret[0], ret[1] |
| |
| # when not found |
| return None, 0 |
| |
| def aux_get(self,mycpv,mylist,strict=0,metacachedir=None,debug=0): |
| "stub code for returning auxilliary db information, such as SLOT, DEPEND, etc." |
| 'input: "sys-apps/foo-1.0",["SLOT","DEPEND","HOMEPAGE"]' |
| 'return: ["0",">=sys-libs/bar-1.0","http://www.foo.com"] or raise KeyError if error' |
| global auxdbkeys,auxdbkeylen |
| |
| cat,pkg = string.split(mycpv, "/", 1) |
| |
| if metacachedir: |
| if cat not in self.metadb: |
| self.metadb[cat] = self.metadbmodule(metacachedir,cat,auxdbkeys,uid,portage_gid) |
| |
| myebuild, mylocation=self.findname2(mycpv) |
| |
| if not myebuild: |
| writemsg("!!! aux_get(): ebuild path for '%(cpv)s' not specified:\n" % {"cpv":mycpv}) |
| writemsg("!!! %s\n" % myebuild) |
| raise KeyError, "'%(cpv)s' at %(path)s" % {"cpv":mycpv,"path":myebuild} |
| |
| myManifestPath = string.join(myebuild.split("/")[:-1],"/")+"/Manifest" |
| if "gpg" in self.mysettings.features: |
| try: |
| mys = portage_gpg.fileStats(myManifestPath) |
| if (myManifestPath in self.manifestCache) and \ |
| (self.manifestCache[myManifestPath] == mys): |
| pass |
| elif self.manifestVerifier: |
| if not self.manifestVerifier.verify(myManifestPath): |
| # Verification failed the desired level. |
| raise portage_exception.UntrustedSignature, "Untrusted Manifest: %(manifest)s" % {"manifest":myManifestPath} |
| |
| if ("severe" in self.mysettings.features) and \ |
| (mys != portage_gpg.fileStats(myManifestPath)): |
| raise portage_exception.SecurityViolation, "Manifest changed: %(manifest)s" % {"manifest":myManifestPath} |
| |
| except portage_exception.InvalidSignature, e: |
| if ("strict" in self.mysettings.features) or \ |
| ("severe" in self.mysettings.features): |
| raise |
| writemsg("!!! INVALID MANIFEST SIGNATURE DETECTED: %(manifest)s\n" % {"manifest":myManifestPath}) |
| except portage_exception.MissingSignature, e: |
| if ("severe" in self.mysettings.features): |
| raise |
| if ("strict" in self.mysettings.features): |
| if myManifestPath not in self.manifestMissingCache: |
| writemsg("!!! WARNING: Missing signature in: %(manifest)s\n" % {"manifest":myManifestPath}) |
| self.manifestMissingCache.insert(0,myManifestPath) |
| except (OSError,portage_exception.FileNotFound), e: |
| if ("strict" in self.mysettings.features) or \ |
| ("severe" in self.mysettings.features): |
| raise portage_exception.SecurityViolation, "Error in verification of signatures: %(errormsg)s" % {"errormsg":str(e)} |
| writemsg("!!! Manifest is missing or inaccessable: %(manifest)s\n" % {"manifest":myManifestPath}) |
| |
| if mylocation not in self.auxdb: |
| self.auxdb[mylocation] = {} |
| |
| if not self.auxdb[mylocation].has_key(cat): |
| self.auxdb[mylocation][cat] = self.auxdbmodule(self.depcachedir+"/"+mylocation,cat,auxdbkeys,uid,portage_gid) |
| |
| if os.access(myebuild, os.R_OK): |
| emtime=os.stat(myebuild)[stat.ST_MTIME] |
| else: |
| writemsg("!!! aux_get(): ebuild for '%(cpv)s' does not exist at:\n" % {"cpv":mycpv}) |
| writemsg("!!! %s\n" % myebuild) |
| raise KeyError |
| |
| try: |
| auxdb_is_valid = self.auxdb[mylocation][cat].has_key(pkg) and \ |
| self.auxdb[mylocation][cat][pkg].has_key("_mtime_") and \ |
| self.auxdb[mylocation][cat][pkg]["_mtime_"] == emtime |
| except SystemExit, e: |
| raise |
| except Exception, e: |
| auxdb_is_valid = 0 |
| if not metacachedir: |
| writemsg("auxdb exception: [%(loc)s]: %(exception)s\n" % {"loc":mylocation+"::"+cat+"/"+pkg, "exception":str(e)}) |
| if self.auxdb[mylocation][cat].has_key(pkg): |
| self.auxdb[mylocation][cat].del_key(pkg) |
| self.auxdb[mylocation][cat].sync() |
| |
| writemsg("auxdb is valid: "+str(auxdb_is_valid)+" "+str(pkg)+"\n", 2) |
| doregen = not (auxdb_is_valid and self.eclassdb.is_current(mylocation,cat,pkg,self.auxdb[mylocation][cat][pkg]["INHERITED"].split())) |
| |
| # when mylocation is not overlay directorys and metacachedir is set, |
| # we use cache files, which is usually on /usr/portage/metadata/cache/. |
| if doregen and mylocation==self.mysettings["PORTDIR"] and metacachedir and self.metadb[cat].has_key(pkg): |
| metadata=self.metadb[cat][pkg] |
| |
| if "EAPI" not in metadata or not metadata["EAPI"].strip(): |
| metadata["EAPI"] = "0" |
| |
| if not eapi_is_supported(metadata["EAPI"]): |
| # intentionally wipe keys. |
| eapi = metadata["EAPI"] |
| mtime = metadata.get("_mtime_", 0) |
| metadata = {} |
| map(lambda x: metadata.setdefault(x, ''), auxdbkeys) |
| metadata["_mtime_"] = long(mtime) |
| metadata["EAPI"] == "-"+eapi |
| |
| else: |
| # eclass updates only if we haven't nuked the entry. |
| self.eclassdb.update_package(mylocation,cat,pkg,metadata["INHERITED"].split()) |
| |
| self.auxdb[mylocation][cat][pkg] = metadata |
| self.auxdb[mylocation][cat].sync() |
| |
| elif doregen: |
| writemsg("doregen: %s %s\n" % (doregen,mycpv), 2) |
| writemsg("Generating cache entry(0) for: "+str(myebuild)+"\n",1) |
| |
| if self.tmpfs: |
| mydbkey = self.tmpfs+"/aux_db_key_temp" |
| else: |
| mydbkey = self.depcachedir+"/aux_db_key_temp" |
| |
| # XXX: Part of the gvisible hack/fix to prevent deadlock |
| # XXX: through doebuild. Need to isolate this somehow... |
| self.mysettings.reset() |
| |
| if self.lock_held: |
| raise "Lock is already held by me?" |
| self.lock_held = 1 |
| mylock = portage_locks.lockfile(mydbkey, wantnewlockfile=1) |
| |
| if os.path.exists(mydbkey): |
| try: |
| os.unlink(mydbkey) |
| except SystemExit, e: |
| raise |
| except Exception, e: |
| portage_locks.unlockfile(mylock) |
| self.lock_held = 0 |
| writemsg("Uncaught handled exception: %(exception)s\n" % {"exception":str(e)}) |
| raise |
| |
| myret=doebuild(myebuild,"depend","/",self.mysettings,dbkey=mydbkey) |
| if myret: |
| portage_locks.unlockfile(mylock) |
| self.lock_held = 0 |
| #depend returned non-zero exit code... |
| writemsg(str(red("\naux_get():")+" (0) Error in "+mycpv+" ebuild. ("+str(myret)+")\n" |
| " Check for syntax error or corruption in the ebuild. (--debug)\n\n")) |
| raise KeyError |
| |
| try: |
| mycent=open(mydbkey,"r") |
| os.unlink(mydbkey) |
| mylines=mycent.readlines() |
| mycent.close() |
| except SystemExit, e: |
| raise |
| except (IOError, OSError): |
| portage_locks.unlockfile(mylock) |
| self.lock_held = 0 |
| writemsg(str(red("\naux_get():")+" (1) Error in "+mycpv+" ebuild.\n" |
| " Check for syntax error or corruption in the ebuild. (--debug)\n\n")) |
| raise KeyError |
| except Exception, e: |
| portage_locks.unlockfile(mylock) |
| self.lock_held = 0 |
| writemsg("Uncaught handled exception: %(exception)s\n" % {"exception":str(e)}) |
| raise |
| |
| portage_locks.unlockfile(mylock) |
| self.lock_held = 0 |
| |
| mydata = {} |
| for x in range(0,len(mylines)): |
| if mylines[x][-1] == '\n': |
| mylines[x] = mylines[x][:-1] |
| mydata[auxdbkeys[x]] = mylines[x] |
| |
| if "EAPI" not in mydata or not mydata["EAPI"].strip(): |
| mydata["EAPI"] = "0" |
| |
| if not eapi_is_supported(mydata["EAPI"]): |
| # if newer version, wipe everything and negate eapi |
| eapi = mydata["EAPI"] |
| mydata = {} |
| map(lambda x:mydata.setdefault(x, ""), auxdbkeys) |
| mydata["EAPI"] = "-"+eapi |
| |
| mydata["_mtime_"] = emtime |
| |
| self.auxdb[mylocation][cat][pkg] = mydata |
| self.auxdb[mylocation][cat].sync() |
| if not self.eclassdb.update_package(mylocation, cat, pkg, mylines[auxdbkeys.index("INHERITED")].split()): |
| sys.exit(1) |
| |
| #finally, we look at our internal cache entry and return the requested data. |
| mydata = self.auxdb[mylocation][cat][pkg] |
| returnme = [] |
| for x in mylist: |
| returnme.append(mydata.get(x,"")) |
| |
| if "EAPI" in mylist: |
| idx = mylist.index("EAPI") |
| if not returnme[idx]: |
| returnme[idx] = "0" |
| |
| return returnme |
| |
| def getfetchlist(self,mypkg,useflags=None,mysettings=None,all=0): |
| if mysettings == None: |
| mysettings = self.mysettings |
| try: |
| myuris = self.aux_get(mypkg,["SRC_URI"])[0] |
| except (IOError,KeyError): |
| print red("getfetchlist():")+" aux_get() error reading "+mypkg+"; aborting." |
| sys.exit(1) |
| |
| if useflags is None: |
| useflags = string.split(mysettings["USE"]) |
| |
| myurilist = portage_dep.paren_reduce(myuris) |
| myurilist = portage_dep.use_reduce(myurilist,uselist=useflags,matchall=all) |
| newuris = flatten(myurilist) |
| |
| myfiles = [] |
| for x in newuris: |
| mya = os.path.basename(x) |
| if not mya in myfiles: |
| myfiles.append(mya) |
| return [newuris, myfiles] |
| |
| def getfetchsizes(self,mypkg,useflags=None,debug=0): |
| # returns a filename:size dictionnary of remaining downloads |
| mydigest=self.finddigest(mypkg) |
| mymd5s=digestParseFile(mydigest) |
| if not mymd5s: |
| if debug: print "[empty/missing/bad digest]: "+mypkg |
| return None |
| filesdict={} |
| if useflags == None: |
| myuris, myfiles = self.getfetchlist(mypkg,all=1) |
| else: |
| myuris, myfiles = self.getfetchlist(mypkg,useflags=useflags) |
| #XXX: maybe this should be improved: take partial downloads |
| # into account? check md5sums? |
| for myfile in myfiles: |
| if debug and myfile not in mymd5s.keys(): |
| print "[bad digest]: missing",myfile,"for",mypkg |
| elif myfile in mymd5s.keys(): |
| distfile=settings["DISTDIR"]+"/"+myfile |
| if not os.access(distfile, os.R_OK): |
| filesdict[myfile]=int(mymd5s[myfile]["size"]) |
| return filesdict |
| |
| def fetch_check(self, mypkg, useflags=None, mysettings=None, all=False): |
| if not useflags: |
| if mysettings: |
| useflags = mysettings["USE"].split() |
| myuri, myfiles = self.getfetchlist(mypkg, useflags=useflags, mysettings=mysettings, all=all) |
| mydigest = self.finddigest(mypkg) |
| mysums = digestParseFile(mydigest) |
| |
| failures = {} |
| for x in myfiles: |
| if not mysums or x not in mysums: |
| ok = False |
| reason = "digest missing" |
| else: |
| ok,reason = portage_checksum.verify_all(self.mysettings["DISTDIR"]+"/"+x, mysums[x]) |
| if not ok: |
| failures[x] = reason |
| if failures: |
| return False |
| return True |
| |
| def getsize(self,mypkg,useflags=None,debug=0): |
| # returns the total size of remaining downloads |
| # |
| # we use getfetchsizes() now, so this function would be obsoleted |
| # |
| filesdict=self.getfetchsizes(mypkg,useflags=useflags,debug=debug) |
| if filesdict==None: |
| return "[empty/missing/bad digest]" |
| mysize=0 |
| for myfile in filesdict.keys(): |
| mysum+=filesdict[myfile] |
| return mysum |
| |
| def cpv_exists(self,mykey): |
| "Tells us whether an actual ebuild exists on disk (no masking)" |
| cps2=mykey.split("/") |
| cps=catpkgsplit(mykey,silent=0) |
| if not cps: |
| #invalid cat/pkg-v |
| return 0 |
| if self.findname(cps[0]+"/"+cps2[1]): |
| return 1 |
| else: |
| return 0 |
| |
| def cp_all(self): |
| "returns a list of all keys in our tree" |
| d={} |
| for x in self.mysettings.categories: |
| for oroot in self.porttrees: |
| for y in listdir(oroot+"/"+x,EmptyOnError=1,ignorecvs=1,dirsonly=1): |
| d[x+"/"+y] = None |
| l = d.keys() |
| l.sort() |
| return l |
| |
| def p_list(self,mycp): |
| d={} |
| for oroot in self.porttrees: |
| for x in listdir(oroot+"/"+mycp,EmptyOnError=1,ignorecvs=1): |
| if x[-7:]==".ebuild": |
| d[x[:-7]] = None |
| return d.keys() |
| |
| def cp_list(self,mycp,use_cache=1): |
| mysplit=mycp.split("/") |
| d={} |
| for oroot in self.porttrees: |
| for x in listdir(oroot+"/"+mycp,EmptyOnError=1,ignorecvs=1): |
| if x[-7:]==".ebuild": |
| d[mysplit[0]+"/"+x[:-7]] = None |
| return d.keys() |
| |
| def freeze(self): |
| for x in ["list-visible","bestmatch-visible","match-visible","match-all"]: |
| self.xcache[x]={} |
| self.frozen=1 |
| |
| def melt(self): |
| self.xcache={} |
| self.frozen=0 |
| |
| def xmatch(self,level,origdep,mydep=None,mykey=None,mylist=None): |
| "caching match function; very trick stuff" |
| #if no updates are being made to the tree, we can consult our xcache... |
| if self.frozen: |
| try: |
| return self.xcache[level][origdep] |
| except KeyError: |
| pass |
| |
| if not mydep: |
| #this stuff only runs on first call of xmatch() |
| #create mydep, mykey from origdep |
| mydep=dep_expand(origdep,mydb=self) |
| mykey=dep_getkey(mydep) |
| |
| if level=="list-visible": |
| #a list of all visible packages, not called directly (just by xmatch()) |
| #myval=self.visible(self.cp_list(mykey)) |
| myval=self.gvisible(self.visible(self.cp_list(mykey))) |
| elif level=="bestmatch-visible": |
| #dep match -- best match of all visible packages |
| myval=best(self.xmatch("match-visible",None,mydep=mydep,mykey=mykey)) |
| #get all visible matches (from xmatch()), then choose the best one |
| elif level=="bestmatch-list": |
| #dep match -- find best match but restrict search to sublist |
| myval=best(match_from_list(mydep,mylist)) |
| #no point is calling xmatch again since we're not caching list deps |
| elif level=="match-list": |
| #dep match -- find all matches but restrict search to sublist (used in 2nd half of visible()) |
| myval=match_from_list(mydep,mylist) |
| elif level=="match-visible": |
| #dep match -- find all visible matches |
| myval=match_from_list(mydep,self.xmatch("list-visible",None,mydep=mydep,mykey=mykey)) |
| #get all visible packages, then get the matching ones |
| elif level=="match-all": |
| #match *all* visible *and* masked packages |
| myval=match_from_list(mydep,self.cp_list(mykey)) |
| else: |
| print "ERROR: xmatch doesn't handle",level,"query!" |
| raise KeyError |
| if self.frozen and (level not in ["match-list","bestmatch-list"]): |
| self.xcache[level][mydep]=myval |
| return myval |
| |
| def match(self,mydep,use_cache=1): |
| return self.xmatch("match-visible",mydep) |
| |
| def visible(self,mylist): |
| """two functions in one. Accepts a list of cpv values and uses the package.mask *and* |
| packages file to remove invisible entries, returning remaining items. This function assumes |
| that all entries in mylist have the same category and package name.""" |
| if (mylist==None) or (len(mylist)==0): |
| return [] |
| newlist=mylist[:] |
| #first, we mask out packages in the package.mask file |
| mykey=newlist[0] |
| cpv=catpkgsplit(mykey) |
| if not cpv: |
| #invalid cat/pkg-v |
| print "visible(): invalid cat/pkg-v:",mykey |
| return [] |
| mycp=cpv[0]+"/"+cpv[1] |
| maskdict=self.mysettings.pmaskdict |
| unmaskdict=self.mysettings.punmaskdict |
| if maskdict.has_key(mycp): |
| for x in maskdict[mycp]: |
| mymatches=self.xmatch("match-all",x) |
| if mymatches==None: |
| #error in package.mask file; print warning and continue: |
| print "visible(): package.mask entry \""+x+"\" is invalid, ignoring..." |
| continue |
| for y in mymatches: |
| unmask=0 |
| if unmaskdict.has_key(mycp): |
| for z in unmaskdict[mycp]: |
| mymatches_unmask=self.xmatch("match-all",z) |
| if y in mymatches_unmask: |
| unmask=1 |
| break |
| if unmask==0: |
| try: |
| newlist.remove(y) |
| except ValueError: |
| pass |
| |
| revmaskdict=self.mysettings.prevmaskdict |
| if revmaskdict.has_key(mycp): |
| for x in revmaskdict[mycp]: |
| #important: only match against the still-unmasked entries... |
| #notice how we pass "newlist" to the xmatch() call below.... |
| #Without this, ~ deps in the packages files are broken. |
| mymatches=self.xmatch("match-list",x,mylist=newlist) |
| if mymatches==None: |
| #error in packages file; print warning and continue: |
| print "emerge: visible(): profile packages entry \""+x+"\" is invalid, ignoring..." |
| continue |
| pos=0 |
| while pos<len(newlist): |
| if newlist[pos] not in mymatches: |
| del newlist[pos] |
| else: |
| pos += 1 |
| return newlist |
| |
| def gvisible(self,mylist): |
| "strip out group-masked (not in current group) entries" |
| global groups |
| if mylist==None: |
| return [] |
| newlist=[] |
| |
| pkgdict = self.mysettings.pkeywordsdict |
| for mycpv in mylist: |
| #we need to update this next line when we have fully integrated the new db api |
| auxerr=0 |
| try: |
| keys, eapi = db["/"]["porttree"].dbapi.aux_get(mycpv, ["KEYWORDS", "EAPI"]) |
| except (KeyError,IOError,TypeError): |
| continue |
| if not keys: |
| # KEYWORDS="" |
| #print "!!! No KEYWORDS for "+str(mycpv)+" -- Untested Status" |
| continue |
| mygroups=keys.split() |
| pgroups=groups[:] |
| match=0 |
| cp = dep_getkey(mycpv) |
| if pkgdict.has_key(cp): |
| matches = match_to_list(mycpv, pkgdict[cp].keys()) |
| for atom in matches: |
| pgroups.extend(pkgdict[cp][atom]) |
| hasstable = False |
| hastesting = False |
| for gp in mygroups: |
| if gp=="*": |
| writemsg("--- WARNING: Package '%s' uses '*' keyword.\n" % mycpv) |
| match=1 |
| break |
| elif "-"+gp in pgroups: |
| match=0 |
| break |
| elif gp in pgroups: |
| match=1 |
| break |
| elif gp[0] == "~": |
| hastesting = True |
| elif gp[0] != "-": |
| hasstable = True |
| if not match and ((hastesting and "~*" in pgroups) or (hasstable and "*" in pgroups)): |
| match=1 |
| if match and eapi_is_supported(eapi): |
| newlist.append(mycpv) |
| return newlist |
| |
| class binarytree(packagetree): |
| "this tree scans for a list of all packages available in PKGDIR" |
| def __init__(self,root,pkgdir,virtual=None,clone=None): |
| |
| if clone: |
| # XXX This isn't cloning. It's an instance of the same thing. |
| self.root=clone.root |
| self.pkgdir=clone.pkgdir |
| self.dbapi=clone.dbapi |
| self.populated=clone.populated |
| self.tree=clone.tree |
| self.remotepkgs=clone.remotepkgs |
| self.invalids=clone.invalids |
| else: |
| self.root=root |
| #self.pkgdir=settings["PKGDIR"] |
| self.pkgdir=pkgdir |
| self.dbapi=bindbapi(self) |
| self.populated=0 |
| self.tree={} |
| self.remotepkgs={} |
| self.invalids=[] |
| |
| def move_ent(self,mylist): |
| if not self.populated: |
| self.populate() |
| origcp=mylist[1] |
| newcp=mylist[2] |
| mynewcat=newcp.split("/")[0] |
| origmatches=self.dbapi.cp_list(origcp) |
| if not origmatches: |
| return |
| for mycpv in origmatches: |
| |
| mycpsplit=catpkgsplit(mycpv) |
| mynewcpv=newcp+"-"+mycpsplit[2] |
| if mycpsplit[3]!="r0": |
| mynewcpv += "-"+mycpsplit[3] |
| myoldpkg=mycpv.split("/")[1] |
| mynewpkg=mynewcpv.split("/")[1] |
| |
| if (mynewpkg != myoldpkg) and os.path.exists(self.getname(mynewcpv)): |
| writemsg("!!! Cannot update binary: Destination exists.\n") |
| writemsg("!!! "+mycpv+" -> "+mynewcpv+"\n") |
| continue |
| |
| tbz2path=self.getname(mycpv) |
| if os.path.exists(tbz2path) and not os.access(tbz2path,os.W_OK): |
| writemsg("!!! Cannot update readonly binary: "+mycpv+"\n") |
| continue |
| |
| #print ">>> Updating data in:",mycpv |
| sys.stdout.write("%") |
| sys.stdout.flush() |
| mytmpdir=settings["PORTAGE_TMPDIR"]+"/tbz2" |
| mytbz2=xpak.tbz2(tbz2path) |
| mytbz2.decompose(mytmpdir, cleanup=1) |
| |
| fixdbentries(origcp, newcp, mytmpdir) |
| |
| catfile=open(mytmpdir+"/CATEGORY", "w") |
| catfile.write(mynewcat+"\n") |
| catfile.close() |
| try: |
| os.rename(mytmpdir+"/"+string.split(mycpv,"/")[1]+".ebuild", mytmpdir+"/"+string.split(mynewcpv, "/")[1]+".ebuild") |
| except SystemExit, e: |
| raise |
| except Exception, e: |
| pass |
| |
| mytbz2.recompose(mytmpdir, cleanup=1) |
| |
| self.dbapi.cpv_remove(mycpv) |
| if (mynewpkg != myoldpkg): |
| os.rename(tbz2path,self.getname(mynewcpv)) |
| self.dbapi.cpv_inject(mynewcpv) |
| return 1 |
| |
| def move_slot_ent(self,mylist,mytmpdir): |
| #mytmpdir=settings["PORTAGE_TMPDIR"]+"/tbz2" |
| mytmpdir=mytmpdir+"/tbz2" |
| if not self.populated: |
| self.populate() |
| pkg=mylist[1] |
| origslot=mylist[2] |
| newslot=mylist[3] |
| origmatches=self.dbapi.match(pkg) |
| if not origmatches: |
| return |
| for mycpv in origmatches: |
| mycpsplit=catpkgsplit(mycpv) |
| myoldpkg=mycpv.split("/")[1] |
| tbz2path=self.getname(mycpv) |
| if os.path.exists(tbz2path) and not os.access(tbz2path,os.W_OK): |
| writemsg("!!! Cannot update readonly binary: "+mycpv+"\n") |
| continue |
| |
| #print ">>> Updating data in:",mycpv |
| mytbz2=xpak.tbz2(tbz2path) |
| mytbz2.decompose(mytmpdir, cleanup=1) |
| |
| slot=grabfile(mytmpdir+"/SLOT"); |
| if (not slot): |
| continue |
| |
| if (slot[0]!=origslot): |
| continue |
| |
| sys.stdout.write("S") |
| sys.stdout.flush() |
| |
| slotfile=open(mytmpdir+"/SLOT", "w") |
| slotfile.write(newslot+"\n") |
| slotfile.close() |
| mytbz2.recompose(mytmpdir, cleanup=1) |
| return 1 |
| |
| def update_ents(self,mybiglist,mytmpdir): |
| #XXX mytmpdir=settings["PORTAGE_TMPDIR"]+"/tbz2" |
| if not self.populated: |
| self.populate() |
| for mycpv in self.dbapi.cp_all(): |
| tbz2path=self.getname(mycpv) |
| if os.path.exists(tbz2path) and not os.access(tbz2path,os.W_OK): |
| writemsg("!!! Cannot update readonly binary: "+mycpv+"\n") |
| continue |
| #print ">>> Updating binary data:",mycpv |
| writemsg("*") |
| mytbz2=xpak.tbz2(tbz2path) |
| mytbz2.decompose(mytmpdir,cleanup=1) |
| for mylist in mybiglist: |
| mylist=string.split(mylist) |
| if mylist[0] != "move": |
| continue |
| fixdbentries(mylist[1], mylist[2], mytmpdir) |
| mytbz2.recompose(mytmpdir,cleanup=1) |
| return 1 |
| |
| def populate(self, getbinpkgs=0,getbinpkgsonly=0): |
| "populates the binarytree" |
| if (not os.path.isdir(self.pkgdir) and not getbinpkgs): |
| return 0 |
| if (not os.path.isdir(self.pkgdir+"/All") and not getbinpkgs): |
| return 0 |
| |
| if (not getbinpkgsonly) and os.path.exists(self.pkgdir+"/All"): |
| for mypkg in listdir(self.pkgdir+"/All"): |
| if mypkg[-5:]!=".tbz2": |
| continue |
| mytbz2=xpak.tbz2(self.pkgdir+"/All/"+mypkg) |
| mycat=mytbz2.getfile("CATEGORY") |
| if not mycat: |
| #old-style or corrupt package |
| writemsg("!!! Invalid binary package: "+mypkg+"\n") |
| self.invalids.append(mypkg) |
| continue |
| mycat=string.strip(mycat) |
| fullpkg=mycat+"/"+mypkg[:-5] |
| mykey=dep_getkey(fullpkg) |
| try: |
| # invalid tbz2's can hurt things. |
| self.dbapi.cpv_inject(fullpkg) |
| except SystemExit, e: |
| raise |
| except: |
| continue |
| |
| if getbinpkgs and not settings["PORTAGE_BINHOST"]: |
| writemsg(red("!!! PORTAGE_BINHOST unset, but use is requested.\n")) |
| |
| if getbinpkgs and settings["PORTAGE_BINHOST"] and not self.remotepkgs: |
| try: |
| chunk_size = long(settings["PORTAGE_BINHOST_CHUNKSIZE"]) |
| if chunk_size < 8: |
| chunk_size = 8 |
| except SystemExit, e: |
| raise |
| except: |
| chunk_size = 3000 |
| |
| writemsg(green("Fetching binary packages info...\n")) |
| self.remotepkgs = getbinpkg.dir_get_metadata(settings["PORTAGE_BINHOST"], chunk_size=chunk_size) |
| writemsg(green(" -- DONE!\n\n")) |
| |
| for mypkg in self.remotepkgs.keys(): |
| if not self.remotepkgs[mypkg].has_key("CATEGORY"): |
| #old-style or corrupt package |
| writemsg("!!! Invalid remote binary package: "+mypkg+"\n") |
| del self.remotepkgs[mypkg] |
| continue |
| mycat=string.strip(self.remotepkgs[mypkg]["CATEGORY"]) |
| fullpkg=mycat+"/"+mypkg[:-5] |
| mykey=dep_getkey(fullpkg) |
| try: |
| # invalid tbz2's can hurt things. |
| #print "cpv_inject("+str(fullpkg)+")" |
| self.dbapi.cpv_inject(fullpkg) |
| #print " -- Injected" |
| except SystemExit, e: |
| raise |
| except: |
| writemsg("!!! Failed to inject remote binary package:"+str(fullpkg)+"\n") |
| del self.remotepkgs[mypkg] |
| continue |
| self.populated=1 |
| |
| def inject(self,cpv): |
| return self.dbapi.cpv_inject(cpv) |
| |
| def exists_specific(self,cpv): |
| if not self.populated: |
| self.populate() |
| return self.dbapi.match(dep_expand("="+cpv,mydb=self.dbapi)) |
| |
| def dep_bestmatch(self,mydep): |
| "compatibility method -- all matches, not just visible ones" |
| if not self.populated: |
| self.populate() |
| writemsg("\n\n", 1) |
| writemsg("mydep: %s\n" % mydep, 1) |
| mydep=dep_expand(mydep,mydb=self.dbapi) |
| writemsg("mydep: %s\n" % mydep, 1) |
| mykey=dep_getkey(mydep) |
| writemsg("mykey: %s\n" % mykey, 1) |
| mymatch=best(match_from_list(mydep,self.dbapi.cp_list(mykey))) |
| writemsg("mymatch: %s\n" % mymatch, 1) |
| if mymatch==None: |
| return "" |
| return mymatch |
| |
| def getname(self,pkgname): |
| "returns file location for this particular package" |
| mysplit=string.split(pkgname,"/") |
| if len(mysplit)==1: |
| return self.pkgdir+"/All/"+self.resolve_specific(pkgname)+".tbz2" |
| else: |
| return self.pkgdir+"/All/"+mysplit[1]+".tbz2" |
| |
| def isremote(self,pkgname): |
| "Returns true if the package is kept remotely." |
| mysplit=string.split(pkgname,"/") |
| remote = (not os.path.exists(self.getname(pkgname))) and self.remotepkgs.has_key(mysplit[1]+".tbz2") |
| return remote |
| |
| def get_use(self,pkgname): |
| mysplit=string.split(pkgname,"/") |
| if self.isremote(pkgname): |
| return string.split(self.remotepkgs[mysplit[1]+".tbz2"]["USE"][:]) |
| tbz2=xpak.tbz2(self.getname(pkgname)) |
| return string.split(tbz2.getfile("USE")) |
| |
| def gettbz2(self,pkgname): |
| "fetches the package from a remote site, if necessary." |
| print "Fetching '"+str(pkgname)+"'" |
| mysplit = string.split(pkgname,"/") |
| tbz2name = mysplit[1]+".tbz2" |
| if not self.isremote(pkgname): |
| if (tbz2name not in self.invalids): |
| return |
| else: |
| writemsg("Resuming download of this tbz2, but it is possible that it is corrupt.\n") |
| mydest = self.pkgdir+"/All/" |
| try: |
| os.makedirs(mydest, 0775) |
| except SystemExit, e: |
| raise |
| except: |
| pass |
| getbinpkg.file_get(settings["PORTAGE_BINHOST"]+"/"+tbz2name, mydest, fcmd=settings["RESUMECOMMAND"]) |
| return |
| |
| def getslot(self,mycatpkg): |
| "Get a slot for a catpkg; assume it exists." |
| myslot = "" |
| try: |
| myslot=self.dbapi.aux_get(mycatpkg,["SLOT"])[0] |
| except SystemExit, e: |
| raise |
| except Exception, e: |
| pass |
| return myslot |
| |
| class dblink: |
| "this class provides an interface to the standard text package database" |
| def __init__(self,cat,pkg,myroot,mysettings): |
| "create a dblink object for cat/pkg. This dblink entry may or may not exist" |
| self.cat = cat |
| self.pkg = pkg |
| self.mycpv = self.cat+"/"+self.pkg |
| self.mysplit = pkgsplit(self.mycpv) |
| |
| self.dbroot = os.path.normpath(myroot+VDB_PATH) |
| self.dbcatdir = self.dbroot+"/"+cat |
| self.dbpkgdir = self.dbcatdir+"/"+pkg |
| self.dbtmpdir = self.dbcatdir+"/-MERGING-"+pkg |
| self.dbdir = self.dbpkgdir |
| |
| self.lock_pkg = None |
| self.lock_tmp = None |
| self.lock_num = 0 # Count of the held locks on the db. |
| |
| self.settings = mysettings |
| if self.settings==1: |
| raise ValueError |
| |
| self.myroot=myroot |
| self.updateprotect() |
| self.contentscache=[] |
| |
| def lockdb(self): |
| if self.lock_num == 0: |
| self.lock_pkg = portage_locks.lockdir(self.dbpkgdir) |
| self.lock_tmp = portage_locks.lockdir(self.dbtmpdir) |
| self.lock_num += 1 |
| |
| def unlockdb(self): |
| self.lock_num -= 1 |
| if self.lock_num == 0: |
| portage_locks.unlockdir(self.lock_tmp) |
| portage_locks.unlockdir(self.lock_pkg) |
| |
| def getpath(self): |
| "return path to location of db information (for >>> informational display)" |
| return self.dbdir |
| |
| def exists(self): |
| "does the db entry exist? boolean." |
| return os.path.exists(self.dbdir) |
| |
| def create(self): |
| "create the skeleton db directory structure. No contents, virtuals, provides or anything. Also will create /var/db/pkg if necessary." |
| # XXXXX Delete this eventually |
| raise Exception, "This is bad. Don't use it." |
| if not os.path.exists(self.dbdir): |
| os.makedirs(self.dbdir) |
| |
| def delete(self): |
| "erase this db entry completely" |
| if not os.path.exists(self.dbdir): |
| return |
| try: |
| for x in listdir(self.dbdir): |
| os.unlink(self.dbdir+"/"+x) |
| os.rmdir(self.dbdir) |
| except OSError, e: |
| print "!!! Unable to remove db entry for this package." |
| print "!!! It is possible that a directory is in this one. Portage will still" |
| print "!!! register this package as installed as long as this directory exists." |
| print "!!! You may delete this directory with 'rm -Rf "+self.dbdir+"'" |
| print "!!! "+str(e) |
| print |
| sys.exit(1) |
| |
| def clearcontents(self): |
| if os.path.exists(self.dbdir+"/CONTENTS"): |
| os.unlink(self.dbdir+"/CONTENTS") |
| |
| def getcontents(self): |
| if not os.path.exists(self.dbdir+"/CONTENTS"): |
| return None |
| if self.contentscache != []: |
| return self.contentscache |
| pkgfiles={} |
| myc=open(self.dbdir+"/CONTENTS","r") |
| mylines=myc.readlines() |
| myc.close() |
| pos=1 |
| for line in mylines: |
| mydat = string.split(line) |
| # we do this so we can remove from non-root filesystems |
| # (use the ROOT var to allow maintenance on other partitions) |
| try: |
| mydat[1]=os.path.normpath(root+mydat[1][1:]) |
| if mydat[0]=="obj": |
| #format: type, mtime, md5sum |
| pkgfiles[string.join(mydat[1:-2]," ")]=[mydat[0], mydat[-1], mydat[-2]] |
| elif mydat[0]=="dir": |
| #format: type |
| pkgfiles[string.join(mydat[1:])]=[mydat[0] ] |
| elif mydat[0]=="sym": |
| #format: type, mtime, dest |
| x=len(mydat)-1 |
| if (x >= 13) and (mydat[-1][-1]==')'): # Old/Broken symlink entry |
| mydat = mydat[:-10]+[mydat[-10:][stat.ST_MTIME][:-1]] |
| writemsg("FIXED SYMLINK LINE: %s\n" % mydat, 1) |
| x=len(mydat)-1 |
| splitter=-1 |
| while(x>=0): |
| if mydat[x]=="->": |
| splitter=x |
| break |
| x=x-1 |
| if splitter==-1: |
| return None |
| pkgfiles[string.join(mydat[1:splitter]," ")]=[mydat[0], mydat[-1], string.join(mydat[(splitter+1):-1]," ")] |
| elif mydat[0]=="dev": |
| #format: type |
| pkgfiles[string.join(mydat[1:]," ")]=[mydat[0] ] |
| elif mydat[0]=="fif": |
| #format: type |
| pkgfiles[string.join(mydat[1:]," ")]=[mydat[0]] |
| else: |
| return None |
| except (KeyError,IndexError): |
| print "portage: CONTENTS line",pos,"corrupt!" |
| pos += 1 |
| self.contentscache=pkgfiles |
| return pkgfiles |
| |
| def updateprotect(self): |
| #do some config file management prep |
| self.protect=[] |
| for x in string.split(self.settings["CONFIG_PROTECT"]): |
| ppath=normalize_path(self.myroot+x)+"/" |
| if os.path.isdir(ppath): |
| self.protect.append(ppath) |
| |
| self.protectmask=[] |
| for x in string.split(self.settings["CONFIG_PROTECT_MASK"]): |
| ppath=normalize_path(self.myroot+x)+"/" |
| if os.path.isdir(ppath): |
| self.protectmask.append(ppath) |
| #if it doesn't exist, silently skip it |
| |
| def isprotected(self,obj): |
| """Checks if obj is in the current protect/mask directories. Returns |
| 0 on unprotected/masked, and 1 on protected.""" |
| masked=0 |
| protected=0 |
| for ppath in self.protect: |
| if (len(ppath) > masked) and (obj[0:len(ppath)]==ppath): |
| protected=len(ppath) |
| #config file management |
| for pmpath in self.protectmask: |
| if (len(pmpath) >= protected) and (obj[0:len(pmpath)]==pmpath): |
| #skip, it's in the mask |
| masked=len(pmpath) |
| return (protected > masked) |
| |
| def unmerge(self,pkgfiles=None,trimworld=1,cleanup=0): |
| global dircache |
| dircache={} |
| |
| self.lockdb() |
| |
| self.settings.load_infodir(self.dbdir) |
| |
| if not pkgfiles: |
| print "No package files given... Grabbing a set." |
| pkgfiles=self.getcontents() |
| |
| # Now, don't assume that the name of the ebuild is the same as the |
| # name of the dir; the package may have been moved. |
| myebuildpath=None |
| |
| # We should use the environement file if possible, |
| # as it has all sourced files already included. |
| # XXX: Need to ensure it doesn't overwrite any important vars though. |
| if os.access(self.dbdir+"/environment.bz2", os.R_OK): |
| spawn("bzip2 -d "+self.dbdir+"/environment.bz2",self.settings,free=1) |
| |
| if not myebuildpath: |
| mystuff=listdir(self.dbdir,EmptyOnError=1) |
| for x in mystuff: |
| if x[-7:]==".ebuild": |
| myebuildpath=self.dbdir+"/"+x |
| break |
| |
| #do prerm script |
| if myebuildpath and os.path.exists(myebuildpath): |
| a=doebuild(myebuildpath,"prerm",self.myroot,self.settings,cleanup=cleanup,use_cache=0,tree="vartree") |
| # XXX: Decide how to handle failures here. |
| if a != 0: |
| writemsg("!!! FAILED prerm: "+str(a)+"\n") |
| sys.exit(123) |
| |
| if pkgfiles: |
| mykeys=pkgfiles.keys() |
| mykeys.sort() |
| mykeys.reverse() |
| |
| self.updateprotect() |
| |
| #process symlinks second-to-last, directories last. |
| mydirs=[] |
| mysyms=[] |
| modprotect="/lib/modules/" |
| for obj in mykeys: |
| obj=os.path.normpath(obj) |
| if obj[:2]=="//": |
| obj=obj[1:] |
| if not os.path.exists(obj): |
| if not os.path.islink(obj): |
| #we skip this if we're dealing with a symlink |
| #because os.path.exists() will operate on the |
| #link target rather than the link itself. |
| print "--- !found "+str(pkgfiles[obj][0]), obj |
| continue |
| # next line includes a tweak to protect modules from being unmerged, |
| # but we don't protect modules from being overwritten if they are |
| # upgraded. We effectively only want one half of the config protection |
| # functionality for /lib/modules. For portage-ng both capabilities |
| # should be able to be independently specified. |
| if self.isprotected(obj) or ((len(obj) > len(modprotect)) and (obj[0:len(modprotect)]==modprotect)): |
| print "--- cfgpro "+str(pkgfiles[obj][0]), obj |
| continue |
| |
| lstatobj=os.lstat(obj) |
| lmtime=str(lstatobj[stat.ST_MTIME]) |
| if (pkgfiles[obj][0] not in ("dir","fif","dev","sym")) and (lmtime != pkgfiles[obj][1]): |
| print "--- !mtime", pkgfiles[obj][0], obj |
| continue |
| |
| if pkgfiles[obj][0]=="dir": |
| if not os.path.isdir(obj): |
| print "--- !dir ","dir", obj |
| continue |
| mydirs.append(obj) |
| elif pkgfiles[obj][0]=="sym": |
| if not os.path.islink(obj): |
| print "--- !sym ","sym", obj |
| continue |
| mysyms.append(obj) |
| elif pkgfiles[obj][0]=="obj": |
| if not os.path.isfile(obj): |
| print "--- !obj ","obj", obj |
| continue |
| mymd5=portage_checksum.perform_md5(obj, calc_prelink=1) |
| |
| # string.lower is needed because db entries used to be in upper-case. The |
| # string.lower allows for backwards compatibility. |
| if mymd5 != string.lower(pkgfiles[obj][2]): |
| print "--- !md5 ","obj", obj |
| continue |
| try: |
| os.unlink(obj) |
| except (OSError,IOError),e: |
| pass |
| print "<<< ","obj",obj |
| elif pkgfiles[obj][0]=="fif": |
| if not stat.S_ISFIFO(lstatobj[stat.ST_MODE]): |
| print "--- !fif ","fif", obj |
| continue |
| try: |
| os.unlink(obj) |
| except (OSError,IOError),e: |
| pass |
| print "<<< ","fif",obj |
| elif pkgfiles[obj][0]=="dev": |
| print "--- ","dev",obj |
| |
| #Now, we need to remove symlinks and directories. We'll repeatedly |
| #remove dead symlinks, then directories until we stop making progress. |
| #This is how we'll clean up directories containing symlinks pointing to |
| #directories that are now empty. These cases will require several |
| #iterations through our two-stage symlink/directory cleaning loop. |
| |
| #main symlink and directory removal loop: |
| |
| #progress -- are we making progress? Initialized to 1 so loop will start |
| progress=1 |
| while progress: |
| #let's see if we're able to make progress this iteration... |
| progress=0 |
| |
| #step 1: remove all the dead symlinks we can... |
| |
| pos = 0 |
| while pos<len(mysyms): |
| obj=mysyms[pos] |
| if os.path.exists(obj): |
| pos += 1 |
| else: |
| #we have a dead symlink; remove it from our list, then from existence |
| del mysyms[pos] |
| #we've made progress! |
| progress = 1 |
| try: |
| os.unlink(obj) |
| print "<<< ","sym",obj |
| except (OSError,IOError),e: |
| print "!!! ","sym",obj |
| #immutable? |
| pass |
| |
| #step 2: remove all the empty directories we can... |
| |
| pos = 0 |
| while pos<len(mydirs): |
| obj=mydirs[pos] |
| objld=listdir(obj) |
| |
| if objld == None: |
| print "mydirs["+str(pos)+"]",mydirs[pos] |
| print "obj",obj |
| print "objld",objld |
| # the directory doesn't exist yet, continue |
| pos += 1 |
| continue |
| |
| if len(objld)>0: |
| #we won't remove this directory (yet), continue |
| pos += 1 |
| continue |
| elif (objld != None): |
| #zappo time |
| del mydirs[pos] |
| #we've made progress! |
| progress = 1 |
| try: |
| os.rmdir(obj) |
| print "<<< ","dir",obj |
| except (OSError,IOError),e: |
| #immutable? |
| pass |
| #else: |
| # print "--- !empty","dir", obj |
| # continue |
| |
| #step 3: if we've made progress, we'll give this another go... |
| |
| #step 4: otherwise, we'll print out the remaining stuff that we didn't unmerge (and rightly so!) |
| |
| #directories that aren't empty: |
| for x in mydirs: |
| print "--- !empty dir", x |
| |
| #symlinks whose target still exists: |
| for x in mysyms: |
| print "--- !targe sym", x |
| |
| #step 5: well, removal of package objects is complete, now for package *meta*-objects.... |
| |
| #remove self from vartree database so that our own virtual gets zapped if we're the last node |
| db[self.myroot]["vartree"].zap(self.mycpv) |
| |
| # New code to remove stuff from the world and virtuals files when unmerged. |
| if trimworld: |
| worldlist=grabfile(self.myroot+WORLD_FILE) |
| mykey=cpv_getkey(self.mycpv) |
| newworldlist=[] |
| for x in worldlist: |
| if dep_getkey(x)==mykey: |
| matches=db[self.myroot]["vartree"].dbapi.match(x,use_cache=0) |
| if not matches: |
| #zap our world entry |
| pass |
| elif (len(matches)==1) and (matches[0]==self.mycpv): |
| #zap our world entry |
| pass |
| else: |
| #others are around; keep it. |
| newworldlist.append(x) |
| else: |
| #this doesn't match the package we're unmerging; keep it. |
| newworldlist.append(x) |
| |
| # if the base dir doesn't exist, create it. |
| # (spanky noticed bug) |
| # XXX: dumb question, but abstracting the root uid might be wise/useful for |
| # 2nd pkg manager installation setups. |
| if not os.path.exists(os.path.dirname(self.myroot+WORLD_FILE)): |
| pdir = os.path.dirname(self.myroot + WORLD_FILE) |
| os.makedirs(pdir, mode=0755) |
| os.chown(pdir, 0, portage_gid) |
| os.chmod(pdir, 02770) |
| |
| myworld=open(self.myroot+WORLD_FILE,"w") |
| for x in newworldlist: |
| myworld.write(x+"\n") |
| myworld.close() |
| |
| #do original postrm |
| if myebuildpath and os.path.exists(myebuildpath): |
| # XXX: This should be the old config, not the current one. |
| # XXX: Use vardbapi to load up env vars. |
| a=doebuild(myebuildpath,"postrm",self.myroot,self.settings,use_cache=0,tree="vartree") |
| # XXX: Decide how to handle failures here. |
| if a != 0: |
| writemsg("!!! FAILED postrm: "+str(a)+"\n") |
| sys.exit(123) |
| |
| self.unlockdb() |
| |
| def isowner(self,filename,destroot): |
| """ check if filename is a new file or belongs to this package |
| (for this or a previous version)""" |
| destfile = os.path.normpath(destroot+"/"+filename) |
| if not os.path.exists(destfile): |
| return True |
| if self.getcontents() and filename in self.getcontents().keys(): |
| return True |
| |
| return False |
| |
| def treewalk(self,srcroot,destroot,inforoot,myebuild,cleanup=0): |
| global db |
| # srcroot = ${D}; |
| # destroot = where to merge, ie. ${ROOT}, |
| # inforoot = root of db entry, |
| # secondhand = list of symlinks that have been skipped due to |
| # their target not existing (will merge later), |
| |
| if not os.path.exists(self.dbcatdir): |
| os.makedirs(self.dbcatdir) |
| |
| # This blocks until we can get the dirs to ourselves. |
| self.lockdb() |
| |
| otherversions=[] |
| for v in db[self.myroot]["vartree"].dbapi.cp_list(self.mysplit[0]): |
| otherversions.append(v.split("/")[1]) |
| |
| # check for package collisions |
| if "collision-protect" in features: |
| myfilelist = listdir(srcroot, recursive=1, filesonly=1, followSymlinks=False) |
| |
| # the linkcheck only works if we are in srcroot |
| mycwd = os.getcwd() |
| os.chdir(srcroot) |
| mysymlinks = filter(os.path.islink, listdir(srcroot, recursive=1, filesonly=0, followSymlinks=False)) |
| |
| stopmerge=False |
| starttime=time.time() |
| i=0 |
| |
| otherpkg=[] |
| mypkglist=[] |
| |
| if self.pkg in otherversions: |
| otherversions.remove(self.pkg) # we already checked this package |
| |
| for v in otherversions: |
| # should we check for same SLOT here ? |
| mypkglist.append(dblink(self.cat,v,destroot,self.settings)) |
| |
| print green("*")+" checking "+str(len(myfilelist))+" files for package collisions" |
| for f in myfilelist: |
| nocheck = False |
| # listdir isn't intelligent enough to exclude symlinked dirs, |
| # so we have to do it ourself |
| for s in mysymlinks: |
| # the length comparison makes sure that the symlink itself is checked |
| if f[:len(s)] == s and len(f) > len(s): |
| nocheck = True |
| if nocheck: |
| continue |
| i=i+1 |
| if i % 1000 == 0: |
| print str(i)+" files checked ..." |
| if f[0] != "/": |
| f="/"+f |
| isowned = False |
| for ver in [self]+mypkglist: |
| if (ver.isowner(f, destroot) or ver.isprotected(f)): |
| isowned = True |
| break |
| if not isowned: |
| print "existing file "+f+" is not owned by this package" |
| stopmerge=True |
| print green("*")+" spent "+str(time.time()-starttime)+" seconds checking for file collisions" |
| if stopmerge: |
| print red("*")+" This package is blocked because it wants to overwrite" |
| print red("*")+" files belonging to other packages (see messages above)." |
| print red("*")+" If you have no clue what this is all about report it " |
| print red("*")+" as a bug for this package on http://bugs.gentoo.org" |
| print |
| print red("package "+self.cat+"/"+self.pkg+" NOT merged") |
| print |
| # Why is the package already merged here db-wise? Shouldn't be the case |
| # only unmerge if it ia new package and has no contents |
| if not self.getcontents(): |
| self.unmerge() |
| self.delete() |
| self.unlockdb() |
| sys.exit(1) |
| try: |
| os.chdir(mycwd) |
| except SystemExit, e: |
| raise |
| except: |
| pass |
| |
| |
| # get old contents info for later unmerging |
| oldcontents = self.getcontents() |
| |
| self.dbdir = self.dbtmpdir |
| self.delete() |
| if not os.path.exists(self.dbtmpdir): |
| os.makedirs(self.dbtmpdir) |
| |
| print ">>> Merging",self.mycpv,"to",destroot |
| |
| # run preinst script |
| if myebuild: |
| # if we are merging a new ebuild, use *its* pre/postinst rather than using the one in /var/db/pkg |
| # (if any). |
| a=doebuild(myebuild,"preinst",root,self.settings,cleanup=cleanup,use_cache=0) |
| else: |
| a=doebuild(inforoot+"/"+self.pkg+".ebuild","preinst",root,self.settings,cleanup=cleanup,use_cache=0) |
| |
| # XXX: Decide how to handle failures here. |
| if a != 0: |
| writemsg("!!! FAILED preinst: "+str(a)+"\n") |
| sys.exit(123) |
| |
| # copy "info" files (like SLOT, CFLAGS, etc.) into the database |
| for x in listdir(inforoot): |
| self.copyfile(inforoot+"/"+x) |
| |
| # get current counter value (counter_tick also takes care of incrementing it) |
| # XXX Need to make this destroot, but it needs to be initialized first. XXX |
| # XXX bis: leads to some invalidentry() call through cp_all(). |
| counter = db["/"]["vartree"].dbapi.counter_tick(self.myroot,mycpv=self.mycpv) |
| # write local package counter for recording |
| lcfile = open(self.dbtmpdir+"/COUNTER","w") |
| lcfile.write(str(counter)) |
| lcfile.close() |
| |
| # open CONTENTS file (possibly overwriting old one) for recording |
| outfile=open(self.dbtmpdir+"/CONTENTS","w") |
| |
| self.updateprotect() |
| |
| #if we have a file containing previously-merged config file md5sums, grab it. |
| if os.path.exists(destroot+CONFIG_MEMORY_FILE): |
| cfgfiledict=grabdict(destroot+CONFIG_MEMORY_FILE) |
| else: |
| cfgfiledict={} |
| if self.settings.has_key("NOCONFMEM"): |
| cfgfiledict["IGNORE"]=1 |
| else: |
| cfgfiledict["IGNORE"]=0 |
| |
| # set umask to 0 for merging; back up umask, save old one in prevmask (since this is a global change) |
| mymtime = long(time.time()) |
| prevmask = os.umask(0) |
| secondhand = [] |
| |
| # we do a first merge; this will recurse through all files in our srcroot but also build up a |
| # "second hand" of symlinks to merge later |
| if self.mergeme(srcroot,destroot,outfile,secondhand,"",cfgfiledict,mymtime): |
| return 1 |
| |
| # now, it's time for dealing our second hand; we'll loop until we can't merge anymore. The rest are |
| # broken symlinks. We'll merge them too. |
| lastlen=0 |
| while len(secondhand) and len(secondhand)!=lastlen: |
| # clear the thirdhand. Anything from our second hand that |
| # couldn't get merged will be added to thirdhand. |
| |
| thirdhand=[] |
| self.mergeme(srcroot,destroot,outfile,thirdhand,secondhand,cfgfiledict,mymtime) |
| |
| #swap hands |
| lastlen=len(secondhand) |
| |
| # our thirdhand now becomes our secondhand. It's ok to throw |
| # away secondhand since thirdhand contains all the stuff that |
| # couldn't be merged. |
| secondhand = thirdhand |
| |
| if len(secondhand): |
| # force merge of remaining symlinks (broken or circular; oh well) |
| self.mergeme(srcroot,destroot,outfile,None,secondhand,cfgfiledict,mymtime) |
| |
| #restore umask |
| os.umask(prevmask) |
| |
| #if we opened it, close it |
| outfile.flush() |
| outfile.close() |
| |
| if (oldcontents): |
| print ">>> Safely unmerging already-installed instance..." |
| self.dbdir = self.dbpkgdir |
| self.unmerge(oldcontents,trimworld=0) |
| self.dbdir = self.dbtmpdir |
| print ">>> original instance of package unmerged safely." |
| |
| # We hold both directory locks. |
| self.dbdir = self.dbpkgdir |
| self.delete() |
| movefile(self.dbtmpdir, self.dbpkgdir, mysettings=self.settings) |
| |
| self.unlockdb() |
| |
| #write out our collection of md5sums |
| if cfgfiledict.has_key("IGNORE"): |
| del cfgfiledict["IGNORE"] |
| |
| # XXXX: HACK! PathSpec is very necessary here. |
| if not os.path.exists(destroot+PRIVATE_PATH): |
| os.makedirs(destroot+PRIVATE_PATH) |
| os.chown(destroot+PRIVATE_PATH,os.getuid(),portage_gid) |
| os.chmod(destroot+PRIVATE_PATH,02770) |
| dirlist = prefix_array(listdir(destroot+PRIVATE_PATH),destroot+PRIVATE_PATH+"/") |
| while dirlist: |
| dirlist.sort() |
| dirlist.reverse() # Gets them in file-before basedir order |
| x = dirlist[0] |
| if os.path.isdir(x): |
| dirlist += prefix_array(listdir(x),x+"/") |
| continue |
| os.unlink(destroot+PRIVATE_PATH+"/"+x) |
| |
| mylock = portage_locks.lockfile(destroot+CONFIG_MEMORY_FILE) |
| writedict(cfgfiledict,destroot+CONFIG_MEMORY_FILE) |
| portage_locks.unlockfile(mylock) |
| |
| #do postinst script |
| if myebuild: |
| # if we are merging a new ebuild, use *its* pre/postinst rather than using the one in /var/db/pkg |
| # (if any). |
| a=doebuild(myebuild,"postinst",root,self.settings,use_cache=0) |
| else: |
| a=doebuild(inforoot+"/"+self.pkg+".ebuild","postinst",root,self.settings,use_cache=0) |
| |
| # XXX: Decide how to handle failures here. |
| if a != 0: |
| writemsg("!!! FAILED postinst: "+str(a)+"\n") |
| sys.exit(123) |
| |
| downgrade = False |
| for v in otherversions: |
| if pkgcmp(catpkgsplit(self.pkg)[1:], catpkgsplit(v)[1:]) < 0: |
| downgrade = True |
| |
| #update environment settings, library paths. DO NOT change symlinks. |
| env_update(makelinks=(not downgrade)) |
| #dircache may break autoclean because it remembers the -MERGING-pkg file |
| global dircache |
| if dircache.has_key(self.dbcatdir): |
| del dircache[self.dbcatdir] |
| print ">>>",self.mycpv,"merged." |
| return 0 |
| |
| def mergeme(self,srcroot,destroot,outfile,secondhand,stufftomerge,cfgfiledict,thismtime): |
| srcroot=os.path.normpath("///"+srcroot)+"/" |
| destroot=os.path.normpath("///"+destroot)+"/" |
| # this is supposed to merge a list of files. There will be 2 forms of argument passing. |
| if type(stufftomerge)==types.StringType: |
| #A directory is specified. Figure out protection paths, listdir() it and process it. |
| mergelist=listdir(srcroot+stufftomerge) |
| offset=stufftomerge |
| # We need mydest defined up here to calc. protection paths. This is now done once per |
| # directory rather than once per file merge. This should really help merge performance. |
| # Trailing / ensures that protects/masks with trailing /'s match. |
| mytruncpath="/"+offset+"/" |
| myppath=self.isprotected(mytruncpath) |
| else: |
| mergelist=stufftomerge |
| offset="" |
| for x in mergelist: |
| mysrc=os.path.normpath("///"+srcroot+offset+x) |
| mydest=os.path.normpath("///"+destroot+offset+x) |
| # myrealdest is mydest without the $ROOT prefix (makes a difference if ROOT!="/") |
| myrealdest="/"+offset+x |
| # stat file once, test using S_* macros many times (faster that way) |
| try: |
| mystat=os.lstat(mysrc) |
| except SystemExit, e: |
| raise |
| except OSError, e: |
| writemsg("\n") |
| writemsg(red("!!! ERROR: There appears to be ")+bold("FILE SYSTEM CORRUPTION.")+red(" A file that is listed\n")) |
| writemsg(red("!!! as existing is not capable of being stat'd. If you are using an\n")) |
| writemsg(red("!!! experimental kernel, please boot into a stable one, force an fsck,\n")) |
| writemsg(red("!!! and ensure your filesystem is in a sane state. ")+bold("'shutdown -Fr now'\n")) |
| writemsg(red("!!! File: ")+str(mysrc)+"\n") |
| writemsg(red("!!! Error: ")+str(e)+"\n") |
| sys.exit(1) |
| except Exception, e: |
| writemsg("\n") |
| writemsg(red("!!! ERROR: An unknown error has occurred during the merge process.\n")) |
| writemsg(red("!!! A stat call returned the following error for the following file:")) |
| writemsg( "!!! Please ensure that your filesystem is intact, otherwise report\n") |
| writemsg( "!!! this as a portage bug at bugs.gentoo.org. Append 'emerge info'.\n") |
| writemsg( "!!! File: "+str(mysrc)+"\n") |
| writemsg( "!!! Error: "+str(e)+"\n") |
| sys.exit(1) |
| |
| |
| mymode=mystat[stat.ST_MODE] |
| # handy variables; mydest is the target object on the live filesystems; |
| # mysrc is the source object in the temporary install dir |
| try: |
| mydmode=os.lstat(mydest)[stat.ST_MODE] |
| except SystemExit, e: |
| raise |
| except: |
| #dest file doesn't exist |
| mydmode=None |
| |
| if stat.S_ISLNK(mymode): |
| # we are merging a symbolic link |
| myabsto=abssymlink(mysrc) |
| if myabsto[0:len(srcroot)]==srcroot: |
| myabsto=myabsto[len(srcroot):] |
| if myabsto[0]!="/": |
| myabsto="/"+myabsto |
| myto=os.readlink(mysrc) |
| if self.settings and self.settings["D"]: |
| if myto.find(self.settings["D"])==0: |
| myto=myto[len(self.settings["D"]):] |
| # myrealto contains the path of the real file to which this symlink points. |
| # we can simply test for existence of this file to see if the target has been merged yet |
| myrealto=os.path.normpath(os.path.join(destroot,myabsto)) |
| if mydmode!=None: |
| #destination exists |
| if not stat.S_ISLNK(mydmode): |
| if stat.S_ISDIR(mydmode): |
| # directory in the way: we can't merge a symlink over a directory |
| # we won't merge this, continue with next file... |
| continue |
| srctarget = os.path.normpath(os.path.dirname(mysrc)+"/"+myto) |
| if os.path.exists(mysrc) and stat.S_ISDIR(os.stat(mysrc)[stat.ST_MODE]): |
| # Kill file blocking installation of symlink to dir #71787 |
| pass |
| elif self.isprotected(mydest): |
| # Use md5 of the target in ${D} if it exists... |
| if os.path.exists(os.path.normpath(srcroot+myabsto)): |
| mydest = new_protect_filename(myrealdest, newmd5=portage_checksum.perform_md5(srcroot+myabsto)) |
| else: |
| mydest = new_protect_filename(myrealdest, newmd5=portage_checksum.perform_md5(myabsto)) |
| |
| # if secondhand==None it means we're operating in "force" mode and should not create a second hand. |
| if (secondhand!=None) and (not os.path.exists(myrealto)): |
| # either the target directory doesn't exist yet or the target file doesn't exist -- or |
| # the target is a broken symlink. We will add this file to our "second hand" and merge |
| # it later. |
| secondhand.append(mysrc[len(srcroot):]) |
| continue |
| # unlinking no longer necessary; "movefile" will overwrite symlinks atomically and correctly |
| mymtime=movefile(mysrc,mydest,newmtime=thismtime,sstat=mystat, mysettings=self.settings) |
| if mymtime!=None: |
| print ">>>",mydest,"->",myto |
| outfile.write("sym "+myrealdest+" -> "+myto+" "+str(mymtime)+"\n") |
| else: |
| print "!!! Failed to move file." |
| print "!!!",mydest,"->",myto |
| sys.exit(1) |
| elif stat.S_ISDIR(mymode): |
| # we are merging a directory |
| if mydmode!=None: |
| # destination exists |
| |
| if bsd_chflags: |
| # Save then clear flags on dest. |
| dflags=bsd_chflags.lgetflags(mydest) |
| if(bsd_chflags.lchflags(mydest, 0)<0): |
| writemsg("!!! Couldn't clear flags on '"+mydest+"'.\n") |
| |
| if not os.access(mydest, os.W_OK): |
| pkgstuff = pkgsplit(self.pkg) |
| writemsg("\n!!! Cannot write to '"+mydest+"'.\n") |
| writemsg("!!! Please check permissions and directories for broken symlinks.\n") |
| writemsg("!!! You may start the merge process again by using ebuild:\n") |
| writemsg("!!! ebuild "+self.settings["PORTDIR"]+"/"+self.cat+"/"+pkgstuff[0]+"/"+self.pkg+".ebuild merge\n") |
| writemsg("!!! And finish by running this: env-update\n\n") |
| return 1 |
| |
| if stat.S_ISLNK(mydmode) or stat.S_ISDIR(mydmode): |
| # a symlink to an existing directory will work for us; keep it: |
| print "---",mydest+"/" |
| if bsd_chflags: |
| bsd_chflags.lchflags(mydest, dflags) |
| else: |
| # a non-directory and non-symlink-to-directory. Won't work for us. Move out of the way. |
| if movefile(mydest,mydest+".backup", mysettings=self.settings) == None: |
| sys.exit(1) |
| print "bak",mydest,mydest+".backup" |
| #now create our directory |
| if selinux_enabled: |
| sid = selinux.get_sid(mysrc) |
| selinux.secure_mkdir(mydest,sid) |
| else: |
| os.mkdir(mydest) |
| if bsd_chflags: |
| bsd_chflags.lchflags(mydest, dflags) |
| os.chmod(mydest,mystat[0]) |
| os.chown(mydest,mystat[4],mystat[5]) |
| print ">>>",mydest+"/" |
| else: |
| #destination doesn't exist |
| if selinux_enabled: |
| sid = selinux.get_sid(mysrc) |
| selinux.secure_mkdir(mydest,sid) |
| else: |
| os.mkdir(mydest) |
| os.chmod(mydest,mystat[0]) |
| if bsd_chflags: |
| bsd_chflags.lchflags(mydest, bsd_chflags.lgetflags(mysrc)) |
| os.chown(mydest,mystat[4],mystat[5]) |
| print ">>>",mydest+"/" |
| outfile.write("dir "+myrealdest+"\n") |
| # recurse and merge this directory |
| if self.mergeme(srcroot,destroot,outfile,secondhand,offset+x+"/",cfgfiledict,thismtime): |
| return 1 |
| elif stat.S_ISREG(mymode): |
| # we are merging a regular file |
| mymd5=portage_checksum.perform_md5(mysrc,calc_prelink=1) |
| # calculate config file protection stuff |
| mydestdir=os.path.dirname(mydest) |
| moveme=1 |
| zing="!!!" |
| if mydmode!=None: |
| # destination file exists |
| if stat.S_ISDIR(mydmode): |
| # install of destination is blocked by an existing directory with the same name |
| moveme=0 |
| print "!!!",mydest |
| elif stat.S_ISREG(mydmode) or (stat.S_ISLNK(mydmode) and os.path.exists(mydest) and stat.S_ISREG(os.stat(mydest)[stat.ST_MODE])): |
| cfgprot=0 |
| # install of destination is blocked by an existing regular file, |
| # or by a symlink to an existing regular file; |
| # now, config file management may come into play. |
| # we only need to tweak mydest if cfg file management is in play. |
| if myppath: |
| # we have a protection path; enable config file management. |
| destmd5=portage_checksum.perform_md5(mydest,calc_prelink=1) |
| cycled=0 |
| if cfgfiledict.has_key(myrealdest): |
| if destmd5 in cfgfiledict[myrealdest]: |
| #cycle |
| print "cycle" |
| del cfgfiledict[myrealdest] |
| cycled=1 |
| if mymd5==destmd5: |
| #file already in place; simply update mtimes of destination |
| os.utime(mydest,(thismtime,thismtime)) |
| zing="---" |
| moveme=0 |
| elif cycled: |
| #mymd5!=destmd5 and we've cycled; move mysrc into place as a ._cfg file |
| moveme=1 |
| cfgfiledict[myrealdest]=[mymd5] |
| cfgprot=1 |
| elif cfgfiledict.has_key(myrealdest) and (mymd5 in cfgfiledict[myrealdest]): |
| #myd5!=destmd5, we haven't cycled, and the file we're merging has been already merged previously |
| zing="-o-" |
| moveme=cfgfiledict["IGNORE"] |
| cfgprot=cfgfiledict["IGNORE"] |
| else: |
| #mymd5!=destmd5, we haven't cycled, and the file we're merging hasn't been merged before |
| moveme=1 |
| cfgprot=1 |
| if not cfgfiledict.has_key(myrealdest): |
| cfgfiledict[myrealdest]=[] |
| if mymd5 not in cfgfiledict[myrealdest]: |
| cfgfiledict[myrealdest].append(mymd5) |
| # only record the last md5 |
| if len(cfgfiledict[myrealdest])>1: |
| del cfgfiledict[myrealdest][0] |
| |
| if cfgprot: |
| mydest = new_protect_filename(myrealdest, newmd5=mymd5) |
| |
| # whether config protection or not, we merge the new file the |
| # same way. Unless moveme=0 (blocking directory) |
| if moveme: |
| mymtime=movefile(mysrc,mydest,newmtime=thismtime,sstat=mystat, mysettings=self.settings) |
| if mymtime == None: |
| sys.exit(1) |
| zing=">>>" |
| else: |
| mymtime=thismtime |
| # We need to touch the destination so that on --update the |
| # old package won't yank the file with it. (non-cfgprot related) |
| os.utime(myrealdest,(thismtime,thismtime)) |
| zing="---" |
| if self.settings["USERLAND"] == "Darwin" and myrealdest[-2:] == ".a": |
| |
| # XXX kludge, bug #58848; can be killed when portage stops relying on |
| # md5+mtime, and uses refcounts |
| # alright, we've fooled w/ mtime on the file; this pisses off static archives |
| # basically internal mtime != file's mtime, so the linker (falsely) thinks |
| # the archive is stale, and needs to have it's toc rebuilt. |
| |
| myf=open(myrealdest,"r+") |
| |
| # ar mtime field is digits padded with spaces, 12 bytes. |
| lms=str(thismtime+5).ljust(12) |
| myf.seek(0) |
| magic=myf.read(8) |
| if magic != "!<arch>\n": |
| # not an archive (dolib.a from portage.py makes it here fex) |
| myf.close() |
| else: |
| st=os.stat(myrealdest) |
| while myf.tell() < st.st_size - 12: |
| # skip object name |
| myf.seek(16,1) |
| |
| # update mtime |
| myf.write(lms) |
| |
| # skip uid/gid/mperm |
| myf.seek(20,1) |
| |
| # read the archive member's size |
| x=long(myf.read(10)) |
| |
| # skip the trailing newlines, and add the potential |
| # extra padding byte if it's not an even size |
| myf.seek(x + 2 + (x % 2),1) |
| |
| # and now we're at the end. yay. |
| myf.close() |
| mymd5=portage_checksum.perform_md5(myrealdest,calc_prelink=1) |
| os.utime(myrealdest,(thismtime,thismtime)) |
| |
| if mymtime!=None: |
| zing=">>>" |
| outfile.write("obj "+myrealdest+" "+mymd5+" "+str(mymtime)+"\n") |
| print zing,mydest |
| else: |
| # we are merging a fifo or device node |
| zing="!!!" |
| if mydmode==None: |
| # destination doesn't exist |
| if movefile(mysrc,mydest,newmtime=thismtime,sstat=mystat, mysettings=self.settings)!=None: |
| zing=">>>" |
| if stat.S_ISFIFO(mymode): |
| # we don't record device nodes in CONTENTS, |
| # although we do merge them. |
| outfile.write("fif "+myrealdest+"\n") |
| else: |
| sys.exit(1) |
| print zing+" "+mydest |
| |
| def merge(self,mergeroot,inforoot,myroot,myebuild=None,cleanup=0): |
| return self.treewalk(mergeroot,myroot,inforoot,myebuild,cleanup=cleanup) |
| |
| def getstring(self,name): |
| "returns contents of a file with whitespace converted to spaces" |
| if not os.path.exists(self.dbdir+"/"+name): |
| return "" |
| myfile=open(self.dbdir+"/"+name,"r") |
| mydata=string.split(myfile.read()) |
| myfile.close() |
| return string.join(mydata," ") |
| |
| def copyfile(self,fname): |
| shutil.copyfile(fname,self.dbdir+"/"+os.path.basename(fname)) |
| |
| def getfile(self,fname): |
| if not os.path.exists(self.dbdir+"/"+fname): |
| return "" |
| myfile=open(self.dbdir+"/"+fname,"r") |
| mydata=myfile.read() |
| myfile.close() |
| return mydata |
| |
| def setfile(self,fname,data): |
| myfile=open(self.dbdir+"/"+fname,"w") |
| myfile.write(data) |
| myfile.close() |
| |
| def getelements(self,ename): |
| if not os.path.exists(self.dbdir+"/"+ename): |
| return [] |
| myelement=open(self.dbdir+"/"+ename,"r") |
| mylines=myelement.readlines() |
| myreturn=[] |
| for x in mylines: |
| for y in string.split(x[:-1]): |
| myreturn.append(y) |
| myelement.close() |
| return myreturn |
| |
| def setelements(self,mylist,ename): |
| myelement=open(self.dbdir+"/"+ename,"w") |
| for x in mylist: |
| myelement.write(x+"\n") |
| myelement.close() |
| |
| def isregular(self): |
| "Is this a regular package (does it have a CATEGORY file? A dblink can be virtual *and* regular)" |
| return os.path.exists(self.dbdir+"/CATEGORY") |
| |
| def cleanup_pkgmerge(mypkg,origdir): |
| shutil.rmtree(settings["PORTAGE_TMPDIR"]+"/portage-pkg/"+mypkg) |
| if os.path.exists(settings["PORTAGE_TMPDIR"]+"/portage/"+mypkg+"/temp/environment"): |
| os.unlink(settings["PORTAGE_TMPDIR"]+"/portage/"+mypkg+"/temp/environment") |
| os.chdir(origdir) |
| |
| def pkgmerge(mytbz2,myroot,mysettings): |
| """will merge a .tbz2 file, returning a list of runtime dependencies |
| that must be satisfied, or None if there was a merge error. This |
| code assumes the package exists.""" |
| if mytbz2[-5:]!=".tbz2": |
| print "!!! Not a .tbz2 file" |
| return None |
| mypkg=os.path.basename(mytbz2)[:-5] |
| xptbz2=xpak.tbz2(mytbz2) |
| pkginfo={} |
| mycat=xptbz2.getfile("CATEGORY") |
| if not mycat: |
| print "!!! CATEGORY info missing from info chunk, aborting..." |
| return None |
| mycat=mycat.strip() |
| mycatpkg=mycat+"/"+mypkg |
| tmploc=mysettings["PORTAGE_TMPDIR"]+"/portage-pkg/" |
| pkgloc=tmploc+"/"+mypkg+"/bin/" |
| infloc=tmploc+"/"+mypkg+"/inf/" |
| myebuild=tmploc+"/"+mypkg+"/inf/"+os.path.basename(mytbz2)[:-4]+"ebuild" |
| if os.path.exists(tmploc+"/"+mypkg): |
| shutil.rmtree(tmploc+"/"+mypkg,1) |
| os.makedirs(pkgloc) |
| os.makedirs(infloc) |
| print ">>> extracting info" |
| xptbz2.unpackinfo(infloc) |
| # run pkg_setup early, so we can bail out early |
| # (before extracting binaries) if there's a problem |
| origdir=getcwd() |
| os.chdir(pkgloc) |
| |
| mysettings.configdict["pkg"]["CATEGORY"] = mycat; |
| a=doebuild(myebuild,"setup",myroot,mysettings,tree="bintree") |
| print ">>> extracting",mypkg |
| notok=spawn("bzip2 -dqc -- '"+mytbz2+"' | tar xpf -",mysettings,free=1) |
| if notok: |
| print "!!! Error extracting",mytbz2 |
| cleanup_pkgmerge(mypkg,origdir) |
| return None |
| |
| # the merge takes care of pre/postinst and old instance |
| # auto-unmerge, virtual/provides updates, etc. |
| mysettings.load_infodir(infloc) |
| mylink=dblink(mycat,mypkg,myroot,mysettings) |
| mylink.merge(pkgloc,infloc,myroot,myebuild,cleanup=1) |
| |
| if not os.path.exists(infloc+"/RDEPEND"): |
| returnme="" |
| else: |
| #get runtime dependencies |
| a=open(infloc+"/RDEPEND","r") |
| returnme=string.join(string.split(a.read())," ") |
| a.close() |
| cleanup_pkgmerge(mypkg,origdir) |
| return returnme |
| |
| |
| if os.environ.has_key("ROOT"): |
| root=os.environ["ROOT"] |
| if not len(root): |
| root="/" |
| elif root[-1]!="/": |
| root=root+"/" |
| else: |
| root="/" |
| if root != "/": |
| if not os.path.exists(root[:-1]): |
| writemsg("!!! Error: ROOT "+root+" does not exist. Please correct this.\n") |
| writemsg("!!! Exiting.\n\n") |
| sys.exit(1) |
| elif not os.path.isdir(root[:-1]): |
| writemsg("!!! Error: ROOT "+root[:-1]+" is not a directory. Please correct this.\n") |
| writemsg("!!! Exiting.\n\n") |
| sys.exit(1) |
| |
| #create tmp and var/tmp if they don't exist; read config |
| os.umask(0) |
| if not os.path.exists(root+"tmp"): |
| writemsg(">>> "+root+"tmp doesn't exist, creating it...\n") |
| os.mkdir(root+"tmp",01777) |
| if not os.path.exists(root+"var/tmp"): |
| writemsg(">>> "+root+"var/tmp doesn't exist, creating it...\n") |
| try: |
| os.mkdir(root+"var",0755) |
| except (OSError,IOError): |
| pass |
| try: |
| os.mkdir(root+"var/tmp",01777) |
| except SystemExit, e: |
| raise |
| except: |
| writemsg("portage: couldn't create /var/tmp; exiting.\n") |
| sys.exit(1) |
| if not os.path.exists(root+"var/lib/portage"): |
| writemsg(">>> "+root+"var/lib/portage doesn't exist, creating it...\n") |
| try: |
| os.mkdir(root+"var",0755) |
| except (OSError,IOError): |
| pass |
| try: |
| os.mkdir(root+"var/lib",0755) |
| except (OSError,IOError): |
| pass |
| try: |
| os.mkdir(root+"var/lib/portage",02750) |
| except SystemExit, e: |
| raise |
| except: |
| writemsg("portage: couldn't create /var/lib/portage; exiting.\n") |
| sys.exit(1) |
| |
| |
| ##################################### |
| # Deprecation Checks |
| |
| os.umask(022) |
| profiledir=None |
| if os.path.isdir(PROFILE_PATH): |
| profiledir = PROFILE_PATH |
| if "PORTAGE_CALLER" in os.environ and os.environ["PORTAGE_CALLER"] == "emerge" and os.access(DEPRECATED_PROFILE_FILE, os.R_OK): |
| deprecatedfile = open(DEPRECATED_PROFILE_FILE, "r") |
| dcontent = deprecatedfile.readlines() |
| deprecatedfile.close() |
| newprofile = dcontent[0] |
| writemsg(red("\n!!! Your current profile is deprecated and not supported anymore.\n")) |
| writemsg(red("!!! Please upgrade to the following profile if possible:\n")) |
| writemsg(8*" "+green(newprofile)+"\n") |
| if len(dcontent) > 1: |
| writemsg("To upgrade do the following steps:\n") |
| for myline in dcontent[1:]: |
| writemsg(myline) |
| writemsg("\n\n") |
| |
| if os.path.exists(USER_VIRTUALS_FILE): |
| writemsg(red("\n!!! /etc/portage/virtuals is deprecated in favor of\n")) |
| writemsg(red("!!! /etc/portage/profile/virtuals. Please move it to\n")) |
| writemsg(red("!!! this new location.\n\n")) |
| |
| # |
| ##################################### |
| |
| db={} |
| |
| # ============================================================================= |
| # ============================================================================= |
| # ----------------------------------------------------------------------------- |
| # We're going to lock the global config to prevent changes, but we need |
| # to ensure the global settings are right. |
| settings=config(config_profile_path=PROFILE_PATH,config_incrementals=portage_const.INCREMENTALS) |
| |
| # useful info |
| settings["PORTAGE_MASTER_PID"]=str(os.getpid()) |
| settings.backup_changes("PORTAGE_MASTER_PID") |
| # We are disabling user-specific bashrc files. |
| settings["BASH_ENV"] = INVALID_ENV_FILE |
| settings.backup_changes("BASH_ENV") |
| |
| # gets virtual package settings |
| def getvirtuals(myroot): |
| global settings |
| writemsg("--- DEPRECATED call to getvirtual\n") |
| return settings.getvirtuals(myroot) |
| |
| def do_vartree(mysettings): |
| global virts,virts_p |
| virts=mysettings.getvirtuals("/") |
| virts_p={} |
| |
| if virts: |
| myvkeys=virts.keys() |
| for x in myvkeys: |
| vkeysplit=x.split("/") |
| if not virts_p.has_key(vkeysplit[1]): |
| virts_p[vkeysplit[1]]=virts[x] |
| db["/"]={"virtuals":virts,"vartree":vartree("/",virts)} |
| if root!="/": |
| virts=mysettings.getvirtuals(root) |
| db[root]={"virtuals":virts,"vartree":vartree(root,virts)} |
| #We need to create the vartree first, then load our settings, and then set up our other trees |
| |
| usedefaults=settings.use_defs |
| |
| # XXX: This is a circular fix. |
| #do_vartree(settings) |
| #settings.loadVirtuals('/') |
| do_vartree(settings) |
| #settings.loadVirtuals('/') |
| |
| settings.reset() # XXX: Regenerate use after we get a vartree -- GLOBAL |
| |
| |
| # XXX: Might cause problems with root="/" assumptions |
| portdb=portdbapi(settings["PORTDIR"]) |
| |
| settings.lock() |
| # ----------------------------------------------------------------------------- |
| # ============================================================================= |
| # ============================================================================= |
| |
| |
| if 'selinux' in settings["USE"].split(" "): |
| try: |
| import selinux |
| if hasattr(selinux, "enabled"): |
| selinux_enabled = selinux.enabled |
| else: |
| selinux_enabled = 1 |
| except OSError, e: |
| writemsg(red("!!! SELinux not loaded: ")+str(e)+"\n") |
| selinux_enabled=0 |
| except ImportError: |
| writemsg(red("!!! SELinux module not found.")+" Please verify that it was installed.\n") |
| selinux_enabled=0 |
| if selinux_enabled == 0: |
| try: |
| del sys.modules["selinux"] |
| except KeyError: |
| pass |
| else: |
| selinux_enabled=0 |
| |
| cachedirs=[CACHE_PATH] |
| if root!="/": |
| cachedirs.append(root+CACHE_PATH) |
| if not os.environ.has_key("SANDBOX_ACTIVE"): |
| for cachedir in cachedirs: |
| if not os.path.exists(cachedir): |
| os.makedirs(cachedir,0755) |
| writemsg(">>> "+cachedir+" doesn't exist, creating it...\n") |
| if not os.path.exists(cachedir+"/dep"): |
| os.makedirs(cachedir+"/dep",2755) |
| writemsg(">>> "+cachedir+"/dep doesn't exist, creating it...\n") |
| try: |
| os.chown(cachedir,uid,portage_gid) |
| os.chmod(cachedir,0775) |
| except OSError: |
| pass |
| try: |
| mystat=os.lstat(cachedir+"/dep") |
| os.chown(cachedir+"/dep",uid,portage_gid) |
| os.chmod(cachedir+"/dep",02775) |
| if mystat[stat.ST_GID]!=portage_gid: |
| spawn("chown -R "+str(uid)+":"+str(portage_gid)+" "+cachedir+"/dep",settings,free=1) |
| spawn("chmod -R u+rw,g+rw "+cachedir+"/dep",settings,free=1) |
| except OSError: |
| pass |
| |
| def flushmtimedb(record): |
| if mtimedb: |
| if record in mtimedb.keys(): |
| del mtimedb[record] |
| #print "mtimedb["+record+"] is cleared." |
| else: |
| writemsg("Invalid or unset record '"+record+"' in mtimedb.\n") |
| |
| #grab mtimes for eclasses and upgrades |
| mtimedb={} |
| mtimedbkeys=[ |
| "updates", "info", |
| "version", "starttime", |
| "resume", "ldpath" |
| ] |
| mtimedbfile=root+"var/cache/edb/mtimedb" |
| try: |
| mypickle=cPickle.Unpickler(open(mtimedbfile)) |
| mypickle.find_global=None |
| mtimedb=mypickle.load() |
| if mtimedb.has_key("old"): |
| mtimedb["updates"]=mtimedb["old"] |
| del mtimedb["old"] |
| if mtimedb.has_key("cur"): |
| del mtimedb["cur"] |
| except SystemExit, e: |
| raise |
| except: |
| #print "!!!",e |
| mtimedb={"updates":{},"version":"","starttime":0} |
| |
| for x in mtimedb.keys(): |
| if x not in mtimedbkeys: |
| writemsg("Deleting invalid mtimedb key: "+str(x)+"\n") |
| del mtimedb[x] |
| |
| #,"porttree":portagetree(root,virts),"bintree":binarytree(root,virts)} |
| features=settings["FEATURES"].split() |
| |
| do_upgrade_packagesmessage=0 |
| def do_upgrade(mykey): |
| global do_upgrade_packagesmessage |
| writemsg("\n\n") |
| writemsg(green("Performing Global Updates: ")+bold(mykey)+"\n") |
| writemsg("(Could take a couple of minutes if you have a lot of binary packages.)\n") |
| writemsg(" "+bold(".")+"='update pass' "+bold("*")+"='binary update' "+bold("@")+"='/var/db move'\n"+" "+bold("s")+"='/var/db SLOT move' "+bold("S")+"='binary SLOT move' "+bold("p")+"='update /etc/portage/package.*'\n") |
| processed=1 |
| #remove stale virtual entries (mappings for packages that no longer exist) |
| |
| update_files={} |
| file_contents={} |
| myxfiles = ["package.mask","package.unmask","package.keywords","package.use"] |
| myxfiles = myxfiles + prefix_array(myxfiles, "profile/") |
| for x in myxfiles: |
| try: |
| myfile = open("/etc/portage/"+x,"r") |
| file_contents[x] = myfile.readlines() |
| myfile.close() |
| except IOError: |
| if file_contents.has_key(x): |
| del file_contents[x] |
| continue |
| |
| worldlist=grabfile("/"+WORLD_FILE) |
| myupd=grabfile(mykey) |
| db["/"]["bintree"]=binarytree("/",settings["PKGDIR"],virts) |
| for myline in myupd: |
| mysplit=myline.split() |
| if not len(mysplit): |
| continue |
| if mysplit[0]!="move" and mysplit[0]!="slotmove": |
| writemsg("portage: Update type \""+mysplit[0]+"\" not recognized.\n") |
| processed=0 |
| continue |
| if mysplit[0]=="move" and len(mysplit)!=3: |
| writemsg("portage: Update command \""+myline+"\" invalid; skipping.\n") |
| processed=0 |
| continue |
| if mysplit[0]=="slotmove" and len(mysplit)!=4: |
| writemsg("portage: Update command \""+myline+"\" invalid; skipping.\n") |
| processed=0 |
| continue |
| sys.stdout.write(".") |
| sys.stdout.flush() |
| |
| if mysplit[0]=="move": |
| db["/"]["vartree"].dbapi.move_ent(mysplit) |
| db["/"]["bintree"].move_ent(mysplit) |
| #update world entries: |
| for x in range(0,len(worldlist)): |
| #update world entries, if any. |
| worldlist[x]=dep_transform(worldlist[x],mysplit[1],mysplit[2]) |
| |
| #update /etc/portage/packages.* |
| for x in file_contents: |
| for mypos in range(0,len(file_contents[x])): |
| line=file_contents[x][mypos] |
| if line[0]=="#" or string.strip(line)=="": |
| continue |
| key=dep_getkey(line.split()[0]) |
| if key==mysplit[1]: |
| file_contents[x][mypos]=string.replace(line,mysplit[1],mysplit[2]) |
| update_files[x]=1 |
| sys.stdout.write("p") |
| sys.stdout.flush() |
| |
| elif mysplit[0]=="slotmove": |
| db["/"]["vartree"].dbapi.move_slot_ent(mysplit) |
| db["/"]["bintree"].move_slot_ent(mysplit,settings["PORTAGE_TMPDIR"]+"/tbz2") |
| |
| for x in update_files: |
| mydblink = dblink('','','/',settings) |
| if mydblink.isprotected("/etc/portage/"+x): |
| updating_file=new_protect_filename("/etc/portage/"+x)[0] |
| else: |
| updating_file="/etc/portage/"+x |
| try: |
| myfile=open(updating_file,"w") |
| myfile.writelines(file_contents[x]) |
| myfile.close() |
| except IOError: |
| continue |
| |
| # We gotta do the brute force updates for these now. |
| if (settings["PORTAGE_CALLER"] in ["fixpackages"]) or \ |
| ("fixpackages" in features): |
| db["/"]["bintree"].update_ents(myupd,settings["PORTAGE_TMPDIR"]+"/tbz2") |
| else: |
| do_upgrade_packagesmessage = 1 |
| |
| if processed: |
| #update our internal mtime since we processed all our directives. |
| mtimedb["updates"][mykey]=os.stat(mykey)[stat.ST_MTIME] |
| myworld=open("/"+WORLD_FILE,"w") |
| for x in worldlist: |
| myworld.write(x+"\n") |
| myworld.close() |
| print "" |
| |
| def portageexit(): |
| global uid,portage_gid,portdb,db |
| if secpass and not os.environ.has_key("SANDBOX_ACTIVE"): |
| # wait child process death |
| try: |
| while True: |
| os.wait() |
| except OSError: |
| #writemsg(">>> All child process are now dead.") |
| pass |
| |
| close_portdbapi_caches() |
| |
| if mtimedb: |
| # Store mtimedb |
| mymfn=mtimedbfile |
| try: |
| mtimedb["version"]=VERSION |
| cPickle.dump(mtimedb, open(mymfn,"w"), -1) |
| except SystemExit, e: |
| raise |
| except Exception, e: |
| pass |
| |
| try: |
| os.chown(mymfn,uid,portage_gid) |
| os.chmod(mymfn,0664) |
| except SystemExit, e: |
| raise |
| except Exception, e: |
| pass |
| |
| atexit.register(portageexit) |
| |
| if (secpass==2) and (not os.environ.has_key("SANDBOX_ACTIVE")): |
| if settings["PORTAGE_CALLER"] in ["emerge","fixpackages"]: |
| #only do this if we're root and not running repoman/ebuild digest |
| updpath=os.path.normpath(settings["PORTDIR"]+"///profiles/updates") |
| didupdate=0 |
| if not mtimedb.has_key("updates"): |
| mtimedb["updates"]={} |
| try: |
| mylist=listdir(updpath,EmptyOnError=1) |
| # resort the list |
| mylist=[myfile[3:]+"-"+myfile[:2] for myfile in mylist] |
| mylist.sort() |
| mylist=[myfile[5:]+"-"+myfile[:4] for myfile in mylist] |
| for myfile in mylist: |
| mykey=updpath+"/"+myfile |
| if not os.path.isfile(mykey): |
| continue |
| if (not mtimedb["updates"].has_key(mykey)) or \ |
| (mtimedb["updates"][mykey] != os.stat(mykey)[stat.ST_MTIME]) or \ |
| (settings["PORTAGE_CALLER"] == "fixpackages"): |
| didupdate=1 |
| do_upgrade(mykey) |
| portageexit() # This lets us save state for C-c. |
| except OSError: |
| #directory doesn't exist |
| pass |
| if didupdate: |
| #make sure our internal databases are consistent; recreate our virts and vartree |
| do_vartree(settings) |
| if do_upgrade_packagesmessage and \ |
| listdir(settings["PKGDIR"]+"/All/",EmptyOnError=1): |
| writemsg("\n\n\n ** Skipping packages. Run 'fixpackages' or set it in FEATURES to fix the") |
| writemsg("\n tbz2's in the packages directory. "+bold("Note: This can take a very long time.")) |
| writemsg("\n") |
| |
| |
| |
| |
| |
| #continue setting up other trees |
| db["/"]["porttree"]=portagetree("/",virts) |
| db["/"]["bintree"]=binarytree("/",settings["PKGDIR"],virts) |
| if root!="/": |
| db[root]["porttree"]=portagetree(root,virts) |
| db[root]["bintree"]=binarytree(root,settings["PKGDIR"],virts) |
| |
| profileroots = [settings["PORTDIR"]+"/profiles/"] |
| for x in settings["PORTDIR_OVERLAY"].split(): |
| profileroots.insert(0, x+"/profiles/") |
| thirdparty_lists = grab_multiple("thirdpartymirrors", profileroots, grabdict) |
| thirdpartymirrors = stack_dictlist(thirdparty_lists, incremental=True) |
| |
| if not os.path.exists(settings["PORTAGE_TMPDIR"]): |
| writemsg("portage: the directory specified in your PORTAGE_TMPDIR variable, \""+settings["PORTAGE_TMPDIR"]+",\"\n") |
| writemsg("does not exist. Please create this directory or correct your PORTAGE_TMPDIR setting.\n") |
| sys.exit(1) |
| if not os.path.isdir(settings["PORTAGE_TMPDIR"]): |
| writemsg("portage: the directory specified in your PORTAGE_TMPDIR variable, \""+settings["PORTAGE_TMPDIR"]+",\"\n") |
| writemsg("is not a directory. Please correct your PORTAGE_TMPDIR setting.\n") |
| sys.exit(1) |
| |
| # COMPATABILITY -- This shouldn't be used. |
| pkglines = settings.packages |
| |
| groups=settings["ACCEPT_KEYWORDS"].split() |
| archlist=[] |
| for myarch in grabfile(settings["PORTDIR"]+"/profiles/arch.list"): |
| archlist += [myarch,"~"+myarch] |
| for group in groups: |
| if not archlist: |
| writemsg("--- 'profiles/arch.list' is empty or not available. Empty portage tree?\n") |
| break |
| elif (group not in archlist) and group[0]!='-': |
| writemsg("\n"+red("!!! INVALID ACCEPT_KEYWORDS: ")+str(group)+"\n") |
| |
| # Clear the cache |
| dircache={} |
| |
| if not os.path.islink(PROFILE_PATH) and os.path.exists(settings["PORTDIR"]+"/profiles"): |
| writemsg(red("\a\n\n!!! "+PROFILE_PATH+" is not a symlink and will probably prevent most merges.\n")) |
| writemsg(red("!!! It should point into a profile within %s/profiles/\n" % settings["PORTDIR"])) |
| writemsg(red("!!! (You can safely ignore this message when syncing. It's harmless.)\n\n\n")) |
| time.sleep(3) |
| |
| # ============================================================================ |
| # ============================================================================ |
| |