--- pym/portage.orig 2003-11-05 00:34:46.000000000 +0100 +++ pym/portage.py 2003-11-05 11:00:26.729303069 +0100 @@ -180,7 +180,8 @@ writemsg(red("*** Please add this user to the portage group if you wish to use portage.\n")) writemsg("\n") -incrementals=["USE","FEATURES","ACCEPT_KEYWORDS","ACCEPT_LICENSE","CONFIG_PROTECT_MASK","CONFIG_PROTECT","PRELINK_PATH","PRELINK_PATH_MASK"] +#merge_exclude: $MERGE_EXCLUDE obviously should be incremental. Now, should it be sticky? I don't see why. +incrementals=["USE","FEATURES","ACCEPT_KEYWORDS","ACCEPT_LICENSE","CONFIG_PROTECT_MASK","CONFIG_PROTECT","PRELINK_PATH","PRELINK_PATH_MASK","MERGE_EXCLUDE"] stickies=["KEYWORDS_ACCEPT","USE","CFLAGS","CXXFLAGS","MAKEOPTS","EXTRA_ECONF","EXTRA_EMAKE"] def getcwd(): @@ -4602,6 +4603,44 @@ masked=len(pmpath) return (protected > masked) + #merge_exclude: create the global exclude paths list from config + # we append a "/" to all elements, including filenames, to avoid + # matching "/path/foobar" when "/path/foo" is the one to exclude + def updateexclude(self): + self.exclude=map((lambda p: os.path.normpath(self.myroot+"/"+p)+"/"), \ + string.split(settings["MERGE_EXCLUDE"])) + + #merge_exclude: exclusion predicate + # for optimisation, we may use a partial exclusion list (optional third argument) + def isexcluded(self,obj,excl=None): + """Checks if obj should be excluded from merge. + Optional parameter overides the global exclude lists. + Returns 1 if obj should be exclude, 0 otherwise.""" + if excl is None: + excl=self.exclude + myobj=os.path.normpath(obj)+"/" + excluded=reduce((lambda r,p: (r or ((len(myobj) >= len(p)) \ + and (myobj[0:len(p)]==p)))), \ + excl, 0) + return excluded + + #merge_exclude: this is an utility function for optimisation + # we will consider only paths that are relevant for the currently being merged directory, + def directsubpaths(self,pathslist,path): + """Given a list of paths (normpathed with a trailing slash) l, and a path p, + returns the list of paths from l that are direct strict subpaths of p""" + mypath=os.path.normpath(path) + mynewpathslist=filter((lambda p: (mypath == os.path.split(p[:-1])[0])), pathslist) + return mynewpathslist + #merge_exclude: and this is another optimisation utility function + # Recursing through the directories to merge, we will only keep part of the exclude list + def subpaths(self,pathslist,path): + """Given a list of paths (normpathed with a trailing slash) l, and a path p, + returns the list of paths from l that are strict subpaths of p""" + mypath=os.path.normpath(path)+"/" + mynewpathslist=filter((lambda p: ((len(p) > len(mypath)) and (p[0:len(mypath)]==mypath))), pathslist) + return mynewpathslist + def unmerge(self,pkgfiles=None,trimworld=1): global dircache dircache={} @@ -4869,6 +4908,8 @@ outfile=open(inforoot+"/CONTENTS","w") self.updateprotect() + #merge_exclude: set/update the global exclude paths list. + self.updateexclude() #if we have a file containing previously-merged config file md5sums, grab it. if os.path.exists(destroot+"/var/cache/edb/config"): @@ -4895,7 +4936,8 @@ # 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) + #merge_exclude: their should not be anything to exclude here. + 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 @@ -4903,8 +4945,11 @@ secondhand=thirdhand if len(secondhand): # force merge of remaining symlinks (broken or circular; oh well) - self.mergeme(srcroot,destroot,outfile,None,secondhand,cfgfiledict,mymtime) - + #merge_exclude: their should not be anything to exclude here. + # Note that symlinks to excluded files may be merged, despite they are broken. + # I see this as an acceptable behavior, but I could add some checks for this case in mergeme if needed. + self.mergeme(srcroot,destroot,outfile,None,secondhand,cfgfiledict,mymtime,[]) + #restore umask os.umask(prevmask) #if we opened it, close it @@ -5014,8 +5059,14 @@ return new_pfile else: return (new_pfile, old_pfile) - - def mergeme(self,srcroot,destroot,outfile,secondhand,stufftomerge,cfgfiledict,thismtime): + + #merge_exclude: added an optional argument which is the list of paths to exclude. + # At first, I was using this for an other optimisation, which no more makes sense with "directsubpaths". + # I keep it here only for giving an empty list in case of secondhand merge. + def mergeme(self,srcroot,destroot,outfile,secondhand,stufftomerge,cfgfiledict,thismtime,excl=None): + #merge_exclude: give its default value to the exclude paths list if needed + if excl is None: + excl=self.exclude 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. @@ -5028,7 +5079,12 @@ # Trailing / ensures that protects/masks with trailing /'s match. mytruncpath="/"+offset+"/" myppath=self.isprotected(mytruncpath) + #merge_exclude: we reduce or exclude paths list to direct subpaths of current directory + myexclude=self.directsubpaths(excl,mytruncpath) else: + #merge_exclude: this can only occur for "secondhand" merges, right? + # so there are no more optimisation to do, secondhand comes with excl=[]. + myexclude=excl mergelist=stufftomerge offset="" for x in mergelist: @@ -5036,6 +5090,17 @@ mydest=os.path.normpath("///"+destroot+offset+x) # myrealdest is mydest without the $ROOT prefix (makes a difference if ROOT!="/") myrealdest="/"+offset+x + + #merge_exclude: If a destination path is to be excluded, from a syntactical point of view, + # then exclude it. That's all we will do for now. + # Another option would be to take care of symlinks, ie. to forbid merging into existing + # symlinks pointing to existing excluded dir for instance... Not sure it how useful it + # would, will do it later if I find a good reason to. + # Anyway, in both cases, output will be "### /path/to/obj" + if self.isexcluded(mydest,myexclude): + print "###",mydest + continue + # stat file once, test using S_* macros many times (faster that way) try: mystat=os.lstat(mysrc) @@ -5143,7 +5208,9 @@ print ">>>",mydest+"/" outfile.write("dir "+myrealdest+"\n") # recurse and merge this directory - if self.mergeme(srcroot,destroot,outfile,secondhand,offset+x+"/",cfgfiledict,thismtime): + #merge_exclude: for this subdir, we only need part of the exclude list + mysubexcl=self.subpaths(excl,mydest) + if self.mergeme(srcroot,destroot,outfile,secondhand,offset+x+"/",cfgfiledict,thismtime,mysubexcl): return 1 elif S_ISREG(mymode): # we are merging a regular file